Roo/bootstrap/TriggerField.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     getAutoCreate : function(){
2459         
2460         
2461         var bdy = {
2462                 cls : 'modal-body',
2463                 html : this.html || ''
2464         };
2465         
2466         var title = {
2467             tag: 'h4',
2468             cls : 'modal-title',
2469             html : this.title
2470         };
2471         
2472         if(this.specificTitle){
2473             title = this.title;
2474             
2475         };
2476         
2477         var header = [];
2478         if (this.allow_close) {
2479             header.push({
2480                 tag: 'button',
2481                 cls : 'close',
2482                 html : '&times'
2483             });
2484         }
2485         header.push(title);
2486         
2487         var modal = {
2488             cls: "modal",
2489             style : 'display: none',
2490             cn : [
2491                 {
2492                     cls: "modal-dialog",
2493                     cn : [
2494                         {
2495                             cls : "modal-content",
2496                             cn : [
2497                                 {
2498                                     cls : 'modal-header',
2499                                     cn : header
2500                                 },
2501                                 bdy,
2502                                 {
2503                                     cls : 'modal-footer',
2504                                     cn : [
2505                                         {
2506                                             tag: 'div',
2507                                             cls: 'btn-' + this.buttonPosition
2508                                         }
2509                                     ]
2510                                     
2511                                 }
2512                                 
2513                                 
2514                             ]
2515                             
2516                         }
2517                     ]
2518                         
2519                 }
2520             ]
2521         };
2522         
2523         if(this.animate){
2524             modal.cls += ' fade';
2525         }
2526         
2527         return modal;
2528           
2529     },
2530     getChildContainer : function() {
2531          
2532          return this.bodyEl;
2533         
2534     },
2535     getButtonContainer : function() {
2536          return this.el.select('.modal-footer div',true).first();
2537         
2538     },
2539     initEvents : function()
2540     {
2541         if (this.allow_close) {
2542             this.closeEl.on('click', this.hide, this);
2543         }
2544
2545     },
2546     show : function() {
2547         
2548         if (!this.rendered) {
2549             this.render();
2550         }
2551         
2552         this.el.setStyle('display', 'block');
2553         
2554         if(this.animate){
2555             var _this = this;
2556             (function(){ _this.el.addClass('in'); }).defer(50);
2557         }else{
2558             this.el.addClass('in');
2559         }
2560         
2561         // not sure how we can show data in here.. 
2562         //if (this.tmpl) {
2563         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2564         //}
2565         
2566         Roo.get(document.body).addClass("x-body-masked");
2567         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2568         this.maskEl.show();
2569         this.el.setStyle('zIndex', '10001');
2570        
2571         this.fireEvent('show', this);
2572         
2573         
2574     },
2575     hide : function()
2576     {
2577         this.maskEl.hide();
2578         Roo.get(document.body).removeClass("x-body-masked");
2579         this.el.removeClass('in');
2580         
2581         if(this.animate){
2582             var _this = this;
2583             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2584         }else{
2585             this.el.setStyle('display', 'none');
2586         }
2587         
2588         this.fireEvent('hide', this);
2589     },
2590     
2591     addButton : function(str, cb)
2592     {
2593          
2594         
2595         var b = Roo.apply({}, { html : str } );
2596         b.xns = b.xns || Roo.bootstrap;
2597         b.xtype = b.xtype || 'Button';
2598         if (typeof(b.listeners) == 'undefined') {
2599             b.listeners = { click : cb.createDelegate(this)  };
2600         }
2601         
2602         var btn = Roo.factory(b);
2603            
2604         btn.onRender(this.el.select('.modal-footer div').first());
2605         
2606         return btn;   
2607        
2608     },
2609     
2610     setDefaultButton : function(btn)
2611     {
2612         //this.el.select('.modal-footer').()
2613     },
2614     resizeTo: function(w,h)
2615     {
2616         // skip..
2617     },
2618     setContentSize  : function(w, h)
2619     {
2620         
2621     },
2622     onButtonClick: function(btn,e)
2623     {
2624         //Roo.log([a,b,c]);
2625         this.fireEvent('btnclick', btn.name, e);
2626     },
2627      /**
2628      * Set the title of the Dialog
2629      * @param {String} str new Title
2630      */
2631     setTitle: function(str) {
2632         this.titleEl.dom.innerHTML = str;    
2633     },
2634     /**
2635      * Set the body of the Dialog
2636      * @param {String} str new Title
2637      */
2638     setBody: function(str) {
2639         this.bodyEl.dom.innerHTML = str;    
2640     },
2641     /**
2642      * Set the body of the Dialog using the template
2643      * @param {Obj} data - apply this data to the template and replace the body contents.
2644      */
2645     applyBody: function(obj)
2646     {
2647         if (!this.tmpl) {
2648             Roo.log("Error - using apply Body without a template");
2649             //code
2650         }
2651         this.tmpl.overwrite(this.bodyEl, obj);
2652     }
2653     
2654 });
2655
2656
2657 Roo.apply(Roo.bootstrap.Modal,  {
2658     /**
2659          * Button config that displays a single OK button
2660          * @type Object
2661          */
2662         OK :  [{
2663             name : 'ok',
2664             weight : 'primary',
2665             html : 'OK'
2666         }], 
2667         /**
2668          * Button config that displays Yes and No buttons
2669          * @type Object
2670          */
2671         YESNO : [
2672             {
2673                 name  : 'no',
2674                 html : 'No'
2675             },
2676             {
2677                 name  :'yes',
2678                 weight : 'primary',
2679                 html : 'Yes'
2680             }
2681         ],
2682         
2683         /**
2684          * Button config that displays OK and Cancel buttons
2685          * @type Object
2686          */
2687         OKCANCEL : [
2688             {
2689                name : 'cancel',
2690                 html : 'Cancel'
2691             },
2692             {
2693                 name : 'ok',
2694                 weight : 'primary',
2695                 html : 'OK'
2696             }
2697         ],
2698         /**
2699          * Button config that displays Yes, No and Cancel buttons
2700          * @type Object
2701          */
2702         YESNOCANCEL : [
2703             {
2704                 name : 'yes',
2705                 weight : 'primary',
2706                 html : 'Yes'
2707             },
2708             {
2709                 name : 'no',
2710                 html : 'No'
2711             },
2712             {
2713                 name : 'cancel',
2714                 html : 'Cancel'
2715             }
2716         ]
2717 });
2718  
2719  /*
2720  * - LGPL
2721  *
2722  * messagebox - can be used as a replace
2723  * 
2724  */
2725 /**
2726  * @class Roo.MessageBox
2727  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2728  * Example usage:
2729  *<pre><code>
2730 // Basic alert:
2731 Roo.Msg.alert('Status', 'Changes saved successfully.');
2732
2733 // Prompt for user data:
2734 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2735     if (btn == 'ok'){
2736         // process text value...
2737     }
2738 });
2739
2740 // Show a dialog using config options:
2741 Roo.Msg.show({
2742    title:'Save Changes?',
2743    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2744    buttons: Roo.Msg.YESNOCANCEL,
2745    fn: processResult,
2746    animEl: 'elId'
2747 });
2748 </code></pre>
2749  * @singleton
2750  */
2751 Roo.bootstrap.MessageBox = function(){
2752     var dlg, opt, mask, waitTimer;
2753     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2754     var buttons, activeTextEl, bwidth;
2755
2756     
2757     // private
2758     var handleButton = function(button){
2759         dlg.hide();
2760         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2761     };
2762
2763     // private
2764     var handleHide = function(){
2765         if(opt && opt.cls){
2766             dlg.el.removeClass(opt.cls);
2767         }
2768         //if(waitTimer){
2769         //    Roo.TaskMgr.stop(waitTimer);
2770         //    waitTimer = null;
2771         //}
2772     };
2773
2774     // private
2775     var updateButtons = function(b){
2776         var width = 0;
2777         if(!b){
2778             buttons["ok"].hide();
2779             buttons["cancel"].hide();
2780             buttons["yes"].hide();
2781             buttons["no"].hide();
2782             //dlg.footer.dom.style.display = 'none';
2783             return width;
2784         }
2785         dlg.footerEl.dom.style.display = '';
2786         for(var k in buttons){
2787             if(typeof buttons[k] != "function"){
2788                 if(b[k]){
2789                     buttons[k].show();
2790                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2791                     width += buttons[k].el.getWidth()+15;
2792                 }else{
2793                     buttons[k].hide();
2794                 }
2795             }
2796         }
2797         return width;
2798     };
2799
2800     // private
2801     var handleEsc = function(d, k, e){
2802         if(opt && opt.closable !== false){
2803             dlg.hide();
2804         }
2805         if(e){
2806             e.stopEvent();
2807         }
2808     };
2809
2810     return {
2811         /**
2812          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2813          * @return {Roo.BasicDialog} The BasicDialog element
2814          */
2815         getDialog : function(){
2816            if(!dlg){
2817                 dlg = new Roo.bootstrap.Modal( {
2818                     //draggable: true,
2819                     //resizable:false,
2820                     //constraintoviewport:false,
2821                     //fixedcenter:true,
2822                     //collapsible : false,
2823                     //shim:true,
2824                     //modal: true,
2825                   //  width:400,
2826                   //  height:100,
2827                     //buttonAlign:"center",
2828                     closeClick : function(){
2829                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2830                             handleButton("no");
2831                         }else{
2832                             handleButton("cancel");
2833                         }
2834                     }
2835                 });
2836                 dlg.render();
2837                 dlg.on("hide", handleHide);
2838                 mask = dlg.mask;
2839                 //dlg.addKeyListener(27, handleEsc);
2840                 buttons = {};
2841                 this.buttons = buttons;
2842                 var bt = this.buttonText;
2843                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2844                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2845                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2846                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2847                 Roo.log(buttons)
2848                 bodyEl = dlg.bodyEl.createChild({
2849
2850                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2851                         '<textarea class="roo-mb-textarea"></textarea>' +
2852                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2853                 });
2854                 msgEl = bodyEl.dom.firstChild;
2855                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2856                 textboxEl.enableDisplayMode();
2857                 textboxEl.addKeyListener([10,13], function(){
2858                     if(dlg.isVisible() && opt && opt.buttons){
2859                         if(opt.buttons.ok){
2860                             handleButton("ok");
2861                         }else if(opt.buttons.yes){
2862                             handleButton("yes");
2863                         }
2864                     }
2865                 });
2866                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2867                 textareaEl.enableDisplayMode();
2868                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2869                 progressEl.enableDisplayMode();
2870                 var pf = progressEl.dom.firstChild;
2871                 if (pf) {
2872                     pp = Roo.get(pf.firstChild);
2873                     pp.setHeight(pf.offsetHeight);
2874                 }
2875                 
2876             }
2877             return dlg;
2878         },
2879
2880         /**
2881          * Updates the message box body text
2882          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2883          * the XHTML-compliant non-breaking space character '&amp;#160;')
2884          * @return {Roo.MessageBox} This message box
2885          */
2886         updateText : function(text){
2887             if(!dlg.isVisible() && !opt.width){
2888                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2889             }
2890             msgEl.innerHTML = text || '&#160;';
2891       
2892             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2893             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2894             var w = Math.max(
2895                     Math.min(opt.width || cw , this.maxWidth), 
2896                     Math.max(opt.minWidth || this.minWidth, bwidth)
2897             );
2898             if(opt.prompt){
2899                 activeTextEl.setWidth(w);
2900             }
2901             if(dlg.isVisible()){
2902                 dlg.fixedcenter = false;
2903             }
2904             // to big, make it scroll. = But as usual stupid IE does not support
2905             // !important..
2906             
2907             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2908                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2909                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2910             } else {
2911                 bodyEl.dom.style.height = '';
2912                 bodyEl.dom.style.overflowY = '';
2913             }
2914             if (cw > w) {
2915                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2916             } else {
2917                 bodyEl.dom.style.overflowX = '';
2918             }
2919             
2920             dlg.setContentSize(w, bodyEl.getHeight());
2921             if(dlg.isVisible()){
2922                 dlg.fixedcenter = true;
2923             }
2924             return this;
2925         },
2926
2927         /**
2928          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2929          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2930          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2931          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2932          * @return {Roo.MessageBox} This message box
2933          */
2934         updateProgress : function(value, text){
2935             if(text){
2936                 this.updateText(text);
2937             }
2938             if (pp) { // weird bug on my firefox - for some reason this is not defined
2939                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2940             }
2941             return this;
2942         },        
2943
2944         /**
2945          * Returns true if the message box is currently displayed
2946          * @return {Boolean} True if the message box is visible, else false
2947          */
2948         isVisible : function(){
2949             return dlg && dlg.isVisible();  
2950         },
2951
2952         /**
2953          * Hides the message box if it is displayed
2954          */
2955         hide : function(){
2956             if(this.isVisible()){
2957                 dlg.hide();
2958             }  
2959         },
2960
2961         /**
2962          * Displays a new message box, or reinitializes an existing message box, based on the config options
2963          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2964          * The following config object properties are supported:
2965          * <pre>
2966 Property    Type             Description
2967 ----------  ---------------  ------------------------------------------------------------------------------------
2968 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2969                                    closes (defaults to undefined)
2970 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2971                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2972 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2973                                    progress and wait dialogs will ignore this property and always hide the
2974                                    close button as they can only be closed programmatically.
2975 cls               String           A custom CSS class to apply to the message box element
2976 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2977                                    displayed (defaults to 75)
2978 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2979                                    function will be btn (the name of the button that was clicked, if applicable,
2980                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2981                                    Progress and wait dialogs will ignore this option since they do not respond to
2982                                    user actions and can only be closed programmatically, so any required function
2983                                    should be called by the same code after it closes the dialog.
2984 icon              String           A CSS class that provides a background image to be used as an icon for
2985                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2986 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2987 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2988 modal             Boolean          False to allow user interaction with the page while the message box is
2989                                    displayed (defaults to true)
2990 msg               String           A string that will replace the existing message box body text (defaults
2991                                    to the XHTML-compliant non-breaking space character '&#160;')
2992 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2993 progress          Boolean          True to display a progress bar (defaults to false)
2994 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2995 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2996 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2997 title             String           The title text
2998 value             String           The string value to set into the active textbox element if displayed
2999 wait              Boolean          True to display a progress bar (defaults to false)
3000 width             Number           The width of the dialog in pixels
3001 </pre>
3002          *
3003          * Example usage:
3004          * <pre><code>
3005 Roo.Msg.show({
3006    title: 'Address',
3007    msg: 'Please enter your address:',
3008    width: 300,
3009    buttons: Roo.MessageBox.OKCANCEL,
3010    multiline: true,
3011    fn: saveAddress,
3012    animEl: 'addAddressBtn'
3013 });
3014 </code></pre>
3015          * @param {Object} config Configuration options
3016          * @return {Roo.MessageBox} This message box
3017          */
3018         show : function(options)
3019         {
3020             
3021             // this causes nightmares if you show one dialog after another
3022             // especially on callbacks..
3023              
3024             if(this.isVisible()){
3025                 
3026                 this.hide();
3027                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3028                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3029                 Roo.log("New Dialog Message:" +  options.msg )
3030                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3031                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3032                 
3033             }
3034             var d = this.getDialog();
3035             opt = options;
3036             d.setTitle(opt.title || "&#160;");
3037             d.closeEl.setDisplayed(opt.closable !== false);
3038             activeTextEl = textboxEl;
3039             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3040             if(opt.prompt){
3041                 if(opt.multiline){
3042                     textboxEl.hide();
3043                     textareaEl.show();
3044                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3045                         opt.multiline : this.defaultTextHeight);
3046                     activeTextEl = textareaEl;
3047                 }else{
3048                     textboxEl.show();
3049                     textareaEl.hide();
3050                 }
3051             }else{
3052                 textboxEl.hide();
3053                 textareaEl.hide();
3054             }
3055             progressEl.setDisplayed(opt.progress === true);
3056             this.updateProgress(0);
3057             activeTextEl.dom.value = opt.value || "";
3058             if(opt.prompt){
3059                 dlg.setDefaultButton(activeTextEl);
3060             }else{
3061                 var bs = opt.buttons;
3062                 var db = null;
3063                 if(bs && bs.ok){
3064                     db = buttons["ok"];
3065                 }else if(bs && bs.yes){
3066                     db = buttons["yes"];
3067                 }
3068                 dlg.setDefaultButton(db);
3069             }
3070             bwidth = updateButtons(opt.buttons);
3071             this.updateText(opt.msg);
3072             if(opt.cls){
3073                 d.el.addClass(opt.cls);
3074             }
3075             d.proxyDrag = opt.proxyDrag === true;
3076             d.modal = opt.modal !== false;
3077             d.mask = opt.modal !== false ? mask : false;
3078             if(!d.isVisible()){
3079                 // force it to the end of the z-index stack so it gets a cursor in FF
3080                 document.body.appendChild(dlg.el.dom);
3081                 d.animateTarget = null;
3082                 d.show(options.animEl);
3083             }
3084             return this;
3085         },
3086
3087         /**
3088          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3089          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3090          * and closing the message box when the process is complete.
3091          * @param {String} title The title bar text
3092          * @param {String} msg The message box body text
3093          * @return {Roo.MessageBox} This message box
3094          */
3095         progress : function(title, msg){
3096             this.show({
3097                 title : title,
3098                 msg : msg,
3099                 buttons: false,
3100                 progress:true,
3101                 closable:false,
3102                 minWidth: this.minProgressWidth,
3103                 modal : true
3104             });
3105             return this;
3106         },
3107
3108         /**
3109          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3110          * If a callback function is passed it will be called after the user clicks the button, and the
3111          * id of the button that was clicked will be passed as the only parameter to the callback
3112          * (could also be the top-right close button).
3113          * @param {String} title The title bar text
3114          * @param {String} msg The message box body text
3115          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3116          * @param {Object} scope (optional) The scope of the callback function
3117          * @return {Roo.MessageBox} This message box
3118          */
3119         alert : function(title, msg, fn, scope){
3120             this.show({
3121                 title : title,
3122                 msg : msg,
3123                 buttons: this.OK,
3124                 fn: fn,
3125                 scope : scope,
3126                 modal : true
3127             });
3128             return this;
3129         },
3130
3131         /**
3132          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3133          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3134          * You are responsible for closing the message box when the process is complete.
3135          * @param {String} msg The message box body text
3136          * @param {String} title (optional) The title bar text
3137          * @return {Roo.MessageBox} This message box
3138          */
3139         wait : function(msg, title){
3140             this.show({
3141                 title : title,
3142                 msg : msg,
3143                 buttons: false,
3144                 closable:false,
3145                 progress:true,
3146                 modal:true,
3147                 width:300,
3148                 wait:true
3149             });
3150             waitTimer = Roo.TaskMgr.start({
3151                 run: function(i){
3152                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3153                 },
3154                 interval: 1000
3155             });
3156             return this;
3157         },
3158
3159         /**
3160          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3161          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3162          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3163          * @param {String} title The title bar text
3164          * @param {String} msg The message box body text
3165          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166          * @param {Object} scope (optional) The scope of the callback function
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         confirm : function(title, msg, fn, scope){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: this.YESNO,
3174                 fn: fn,
3175                 scope : scope,
3176                 modal : true
3177             });
3178             return this;
3179         },
3180
3181         /**
3182          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3183          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3184          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3185          * (could also be the top-right close button) and the text that was entered will be passed as the two
3186          * parameters to the callback.
3187          * @param {String} title The title bar text
3188          * @param {String} msg The message box body text
3189          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3190          * @param {Object} scope (optional) The scope of the callback function
3191          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3192          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3193          * @return {Roo.MessageBox} This message box
3194          */
3195         prompt : function(title, msg, fn, scope, multiline){
3196             this.show({
3197                 title : title,
3198                 msg : msg,
3199                 buttons: this.OKCANCEL,
3200                 fn: fn,
3201                 minWidth:250,
3202                 scope : scope,
3203                 prompt:true,
3204                 multiline: multiline,
3205                 modal : true
3206             });
3207             return this;
3208         },
3209
3210         /**
3211          * Button config that displays a single OK button
3212          * @type Object
3213          */
3214         OK : {ok:true},
3215         /**
3216          * Button config that displays Yes and No buttons
3217          * @type Object
3218          */
3219         YESNO : {yes:true, no:true},
3220         /**
3221          * Button config that displays OK and Cancel buttons
3222          * @type Object
3223          */
3224         OKCANCEL : {ok:true, cancel:true},
3225         /**
3226          * Button config that displays Yes, No and Cancel buttons
3227          * @type Object
3228          */
3229         YESNOCANCEL : {yes:true, no:true, cancel:true},
3230
3231         /**
3232          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3233          * @type Number
3234          */
3235         defaultTextHeight : 75,
3236         /**
3237          * The maximum width in pixels of the message box (defaults to 600)
3238          * @type Number
3239          */
3240         maxWidth : 600,
3241         /**
3242          * The minimum width in pixels of the message box (defaults to 100)
3243          * @type Number
3244          */
3245         minWidth : 100,
3246         /**
3247          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3248          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3249          * @type Number
3250          */
3251         minProgressWidth : 250,
3252         /**
3253          * An object containing the default button text strings that can be overriden for localized language support.
3254          * Supported properties are: ok, cancel, yes and no.
3255          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3256          * @type Object
3257          */
3258         buttonText : {
3259             ok : "OK",
3260             cancel : "Cancel",
3261             yes : "Yes",
3262             no : "No"
3263         }
3264     };
3265 }();
3266
3267 /**
3268  * Shorthand for {@link Roo.MessageBox}
3269  */
3270 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3271 Roo.Msg = Roo.Msg || Roo.MessageBox;
3272 /*
3273  * - LGPL
3274  *
3275  * navbar
3276  * 
3277  */
3278
3279 /**
3280  * @class Roo.bootstrap.Navbar
3281  * @extends Roo.bootstrap.Component
3282  * Bootstrap Navbar class
3283
3284  * @constructor
3285  * Create a new Navbar
3286  * @param {Object} config The config object
3287  */
3288
3289
3290 Roo.bootstrap.Navbar = function(config){
3291     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3292     
3293 };
3294
3295 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3296     
3297     
3298    
3299     // private
3300     navItems : false,
3301     loadMask : false,
3302     
3303     
3304     getAutoCreate : function(){
3305         
3306         
3307         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3308         
3309     },
3310     
3311     initEvents :function ()
3312     {
3313         //Roo.log(this.el.select('.navbar-toggle',true));
3314         this.el.select('.navbar-toggle',true).on('click', function() {
3315            // Roo.log('click');
3316             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3317         }, this);
3318         
3319         var mark = {
3320             tag: "div",
3321             cls:"x-dlg-mask"
3322         }
3323         
3324         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3325         
3326         var size = this.el.getSize();
3327         this.maskEl.setSize(size.width, size.height);
3328         this.maskEl.enableDisplayMode("block");
3329         this.maskEl.hide();
3330         
3331         if(this.loadMask){
3332             this.maskEl.show();
3333         }
3334     },
3335     
3336     
3337     getChildContainer : function()
3338     {
3339         if (this.el.select('.collapse').getCount()) {
3340             return this.el.select('.collapse',true).first();
3341         }
3342         
3343         return this.el;
3344     },
3345     
3346     mask : function()
3347     {
3348         this.maskEl.show();
3349     },
3350     
3351     unmask : function()
3352     {
3353         this.maskEl.hide();
3354     } 
3355     
3356     
3357     
3358     
3359 });
3360
3361
3362
3363  
3364
3365  /*
3366  * - LGPL
3367  *
3368  * navbar
3369  * 
3370  */
3371
3372 /**
3373  * @class Roo.bootstrap.NavSimplebar
3374  * @extends Roo.bootstrap.Navbar
3375  * Bootstrap Sidebar class
3376  *
3377  * @cfg {Boolean} inverse is inverted color
3378  * 
3379  * @cfg {String} type (nav | pills | tabs)
3380  * @cfg {Boolean} arrangement stacked | justified
3381  * @cfg {String} align (left | right) alignment
3382  * 
3383  * @cfg {Boolean} main (true|false) main nav bar? default false
3384  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3385  * 
3386  * @cfg {String} tag (header|footer|nav|div) default is nav 
3387
3388  * 
3389  * 
3390  * 
3391  * @constructor
3392  * Create a new Sidebar
3393  * @param {Object} config The config object
3394  */
3395
3396
3397 Roo.bootstrap.NavSimplebar = function(config){
3398     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3399 };
3400
3401 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3402     
3403     inverse: false,
3404     
3405     type: false,
3406     arrangement: '',
3407     align : false,
3408     
3409     
3410     
3411     main : false,
3412     
3413     
3414     tag : false,
3415     
3416     
3417     getAutoCreate : function(){
3418         
3419         
3420         var cfg = {
3421             tag : this.tag || 'div',
3422             cls : 'navbar'
3423         };
3424           
3425         
3426         cfg.cn = [
3427             {
3428                 cls: 'nav',
3429                 tag : 'ul'
3430             }
3431         ];
3432         
3433          
3434         this.type = this.type || 'nav';
3435         if (['tabs','pills'].indexOf(this.type)!==-1) {
3436             cfg.cn[0].cls += ' nav-' + this.type
3437         
3438         
3439         } else {
3440             if (this.type!=='nav') {
3441                 Roo.log('nav type must be nav/tabs/pills')
3442             }
3443             cfg.cn[0].cls += ' navbar-nav'
3444         }
3445         
3446         
3447         
3448         
3449         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3450             cfg.cn[0].cls += ' nav-' + this.arrangement;
3451         }
3452         
3453         
3454         if (this.align === 'right') {
3455             cfg.cn[0].cls += ' navbar-right';
3456         }
3457         
3458         if (this.inverse) {
3459             cfg.cls += ' navbar-inverse';
3460             
3461         }
3462         
3463         
3464         return cfg;
3465     
3466         
3467     }
3468     
3469     
3470     
3471 });
3472
3473
3474
3475  
3476
3477  
3478        /*
3479  * - LGPL
3480  *
3481  * navbar
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.NavHeaderbar
3487  * @extends Roo.bootstrap.NavSimplebar
3488  * Bootstrap Sidebar class
3489  *
3490  * @cfg {String} brand what is brand
3491  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3492  * @cfg {String} brand_href href of the brand
3493  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3494  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3495  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3496  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3497  * 
3498  * @constructor
3499  * Create a new Sidebar
3500  * @param {Object} config The config object
3501  */
3502
3503
3504 Roo.bootstrap.NavHeaderbar = function(config){
3505     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3506       
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3510     
3511     position: '',
3512     brand: '',
3513     brand_href: false,
3514     srButton : true,
3515     autohide : false,
3516     desktopCenter : false,
3517    
3518     
3519     getAutoCreate : function(){
3520         
3521         var   cfg = {
3522             tag: this.nav || 'nav',
3523             cls: 'navbar',
3524             role: 'navigation',
3525             cn: []
3526         };
3527         
3528         var cn = cfg.cn;
3529         if (this.desktopCenter) {
3530             cn.push({cls : 'container', cn : []});
3531             cn = cn[0].cn;
3532         }
3533         
3534         if(this.srButton){
3535             cn.push({
3536                 tag: 'div',
3537                 cls: 'navbar-header',
3538                 cn: [
3539                     {
3540                         tag: 'button',
3541                         type: 'button',
3542                         cls: 'navbar-toggle',
3543                         'data-toggle': 'collapse',
3544                         cn: [
3545                             {
3546                                 tag: 'span',
3547                                 cls: 'sr-only',
3548                                 html: 'Toggle navigation'
3549                             },
3550                             {
3551                                 tag: 'span',
3552                                 cls: 'icon-bar'
3553                             },
3554                             {
3555                                 tag: 'span',
3556                                 cls: 'icon-bar'
3557                             },
3558                             {
3559                                 tag: 'span',
3560                                 cls: 'icon-bar'
3561                             }
3562                         ]
3563                     }
3564                 ]
3565             });
3566         }
3567         
3568         cn.push({
3569             tag: 'div',
3570             cls: 'collapse navbar-collapse',
3571             cn : []
3572         });
3573         
3574         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3575         
3576         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3577             cfg.cls += ' navbar-' + this.position;
3578             
3579             // tag can override this..
3580             
3581             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3582         }
3583         
3584         if (this.brand !== '') {
3585             cn[0].cn.push({
3586                 tag: 'a',
3587                 href: this.brand_href ? this.brand_href : '#',
3588                 cls: 'navbar-brand',
3589                 cn: [
3590                 this.brand
3591                 ]
3592             });
3593         }
3594         
3595         if(this.main){
3596             cfg.cls += ' main-nav';
3597         }
3598         
3599         
3600         return cfg;
3601
3602         
3603     },
3604     getHeaderChildContainer : function()
3605     {
3606         if (this.el.select('.navbar-header').getCount()) {
3607             return this.el.select('.navbar-header',true).first();
3608         }
3609         
3610         return this.getChildContainer();
3611     },
3612     
3613     
3614     initEvents : function()
3615     {
3616         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3617         
3618         if (this.autohide) {
3619             
3620             var prevScroll = 0;
3621             var ft = this.el;
3622             
3623             Roo.get(document).on('scroll',function(e) {
3624                 var ns = Roo.get(document).getScroll().top;
3625                 var os = prevScroll;
3626                 prevScroll = ns;
3627                 
3628                 if(ns > os){
3629                     ft.removeClass('slideDown');
3630                     ft.addClass('slideUp');
3631                     return;
3632                 }
3633                 ft.removeClass('slideUp');
3634                 ft.addClass('slideDown');
3635                  
3636               
3637           },this);
3638         }
3639     }    
3640     
3641 });
3642
3643
3644
3645  
3646
3647  /*
3648  * - LGPL
3649  *
3650  * navbar
3651  * 
3652  */
3653
3654 /**
3655  * @class Roo.bootstrap.NavSidebar
3656  * @extends Roo.bootstrap.Navbar
3657  * Bootstrap Sidebar class
3658  * 
3659  * @constructor
3660  * Create a new Sidebar
3661  * @param {Object} config The config object
3662  */
3663
3664
3665 Roo.bootstrap.NavSidebar = function(config){
3666     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3667 };
3668
3669 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3670     
3671     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3672     
3673     getAutoCreate : function(){
3674         
3675         
3676         return  {
3677             tag: 'div',
3678             cls: 'sidebar sidebar-nav'
3679         };
3680     
3681         
3682     }
3683     
3684     
3685     
3686 });
3687
3688
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * nav group
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.NavGroup
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap NavGroup class
3703  * @cfg {String} align (left|right)
3704  * @cfg {Boolean} inverse
3705  * @cfg {String} type (nav|pills|tab) default nav
3706  * @cfg {String} navId - reference Id for navbar.
3707
3708  * 
3709  * @constructor
3710  * Create a new nav group
3711  * @param {Object} config The config object
3712  */
3713
3714 Roo.bootstrap.NavGroup = function(config){
3715     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3716     this.navItems = [];
3717    
3718     Roo.bootstrap.NavGroup.register(this);
3719      this.addEvents({
3720         /**
3721              * @event changed
3722              * Fires when the active item changes
3723              * @param {Roo.bootstrap.NavGroup} this
3724              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3725              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3726          */
3727         'changed': true
3728      });
3729     
3730 };
3731
3732 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3733     
3734     align: '',
3735     inverse: false,
3736     form: false,
3737     type: 'nav',
3738     navId : '',
3739     // private
3740     
3741     navItems : false, 
3742     
3743     getAutoCreate : function()
3744     {
3745         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3746         
3747         cfg = {
3748             tag : 'ul',
3749             cls: 'nav' 
3750         }
3751         
3752         if (['tabs','pills'].indexOf(this.type)!==-1) {
3753             cfg.cls += ' nav-' + this.type
3754         } else {
3755             if (this.type!=='nav') {
3756                 Roo.log('nav type must be nav/tabs/pills')
3757             }
3758             cfg.cls += ' navbar-nav'
3759         }
3760         
3761         if (this.parent().sidebar) {
3762             cfg = {
3763                 tag: 'ul',
3764                 cls: 'dashboard-menu sidebar-menu'
3765             }
3766             
3767             return cfg;
3768         }
3769         
3770         if (this.form === true) {
3771             cfg = {
3772                 tag: 'form',
3773                 cls: 'navbar-form'
3774             }
3775             
3776             if (this.align === 'right') {
3777                 cfg.cls += ' navbar-right';
3778             } else {
3779                 cfg.cls += ' navbar-left';
3780             }
3781         }
3782         
3783         if (this.align === 'right') {
3784             cfg.cls += ' navbar-right';
3785         }
3786         
3787         if (this.inverse) {
3788             cfg.cls += ' navbar-inverse';
3789             
3790         }
3791         
3792         
3793         return cfg;
3794     },
3795     /**
3796     * sets the active Navigation item
3797     * @param {Roo.bootstrap.NavItem} the new current navitem
3798     */
3799     setActiveItem : function(item)
3800     {
3801         var prev = false;
3802         Roo.each(this.navItems, function(v){
3803             if (v == item) {
3804                 return ;
3805             }
3806             if (v.isActive()) {
3807                 v.setActive(false, true);
3808                 prev = v;
3809                 
3810             }
3811             
3812         });
3813
3814         item.setActive(true, true);
3815         this.fireEvent('changed', this, item, prev);
3816         
3817         
3818     },
3819     /**
3820     * gets the active Navigation item
3821     * @return {Roo.bootstrap.NavItem} the current navitem
3822     */
3823     getActive : function()
3824     {
3825         
3826         var prev = false;
3827         Roo.each(this.navItems, function(v){
3828             
3829             if (v.isActive()) {
3830                 prev = v;
3831                 
3832             }
3833             
3834         });
3835         return prev;
3836     },
3837     
3838     indexOfNav : function()
3839     {
3840         
3841         var prev = false;
3842         Roo.each(this.navItems, function(v,i){
3843             
3844             if (v.isActive()) {
3845                 prev = i;
3846                 
3847             }
3848             
3849         });
3850         return prev;
3851     },
3852     /**
3853     * adds a Navigation item
3854     * @param {Roo.bootstrap.NavItem} the navitem to add
3855     */
3856     addItem : function(cfg)
3857     {
3858         var cn = new Roo.bootstrap.NavItem(cfg);
3859         this.register(cn);
3860         cn.parentId = this.id;
3861         cn.onRender(this.el, null);
3862         return cn;
3863     },
3864     /**
3865     * register a Navigation item
3866     * @param {Roo.bootstrap.NavItem} the navitem to add
3867     */
3868     register : function(item)
3869     {
3870         this.navItems.push( item);
3871         item.navId = this.navId;
3872     
3873     },
3874     
3875     /**
3876     * clear all the Navigation item
3877     */
3878    
3879     clearAll : function()
3880     {
3881         this.navItems = [];
3882         this.el.dom.innerHTML = '';
3883     },
3884     
3885     getNavItem: function(tabId)
3886     {
3887         var ret = false;
3888         Roo.each(this.navItems, function(e) {
3889             if (e.tabId == tabId) {
3890                ret =  e;
3891                return false;
3892             }
3893             return true;
3894             
3895         });
3896         return ret;
3897     },
3898     
3899     setActiveNext : function()
3900     {
3901         var i = this.indexOfNav(this.getActive());
3902         if (i > this.navItems.length) {
3903             return;
3904         }
3905         this.setActiveItem(this.navItems[i+1]);
3906     },
3907     setActivePrev : function()
3908     {
3909         var i = this.indexOfNav(this.getActive());
3910         if (i  < 1) {
3911             return;
3912         }
3913         this.setActiveItem(this.navItems[i-1]);
3914     },
3915     clearWasActive : function(except) {
3916         Roo.each(this.navItems, function(e) {
3917             if (e.tabId != except.tabId && e.was_active) {
3918                e.was_active = false;
3919                return false;
3920             }
3921             return true;
3922             
3923         });
3924     },
3925     getWasActive : function ()
3926     {
3927         var r = false;
3928         Roo.each(this.navItems, function(e) {
3929             if (e.was_active) {
3930                r = e;
3931                return false;
3932             }
3933             return true;
3934             
3935         });
3936         return r;
3937     }
3938     
3939     
3940 });
3941
3942  
3943 Roo.apply(Roo.bootstrap.NavGroup, {
3944     
3945     groups: {},
3946      /**
3947     * register a Navigation Group
3948     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3949     */
3950     register : function(navgrp)
3951     {
3952         this.groups[navgrp.navId] = navgrp;
3953         
3954     },
3955     /**
3956     * fetch a Navigation Group based on the navigation ID
3957     * @param {string} the navgroup to add
3958     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3959     */
3960     get: function(navId) {
3961         if (typeof(this.groups[navId]) == 'undefined') {
3962             return false;
3963             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3964         }
3965         return this.groups[navId] ;
3966     }
3967     
3968     
3969     
3970 });
3971
3972  /*
3973  * - LGPL
3974  *
3975  * row
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavItem
3981  * @extends Roo.bootstrap.Component
3982  * Bootstrap Navbar.NavItem class
3983  * @cfg {String} href  link to
3984  * @cfg {String} html content of button
3985  * @cfg {String} badge text inside badge
3986  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3987  * @cfg {String} glyphicon name of glyphicon
3988  * @cfg {String} icon name of font awesome icon
3989  * @cfg {Boolean} active Is item active
3990  * @cfg {Boolean} disabled Is item disabled
3991  
3992  * @cfg {Boolean} preventDefault (true | false) default false
3993  * @cfg {String} tabId the tab that this item activates.
3994  * @cfg {String} tagtype (a|span) render as a href or span?
3995  * @cfg {Boolean} animateRef (true|false) link to element default false  
3996   
3997  * @constructor
3998  * Create a new Navbar Item
3999  * @param {Object} config The config object
4000  */
4001 Roo.bootstrap.NavItem = function(config){
4002     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4003     this.addEvents({
4004         // raw events
4005         /**
4006          * @event click
4007          * The raw click event for the entire grid.
4008          * @param {Roo.EventObject} e
4009          */
4010         "click" : true,
4011          /**
4012             * @event changed
4013             * Fires when the active item active state changes
4014             * @param {Roo.bootstrap.NavItem} this
4015             * @param {boolean} state the new state
4016              
4017          */
4018         'changed': true,
4019         /**
4020             * @event scrollto
4021             * Fires when scroll to element
4022             * @param {Roo.bootstrap.NavItem} this
4023             * @param {Object} options
4024             * @param {Roo.EventObject} e
4025              
4026          */
4027         'scrollto': true
4028     });
4029    
4030 };
4031
4032 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4033     
4034     href: false,
4035     html: '',
4036     badge: '',
4037     icon: false,
4038     glyphicon: false,
4039     active: false,
4040     preventDefault : false,
4041     tabId : false,
4042     tagtype : 'a',
4043     disabled : false,
4044     animateRef : false,
4045     was_active : false,
4046     
4047     getAutoCreate : function(){
4048          
4049         var cfg = {
4050             tag: 'li',
4051             cls: 'nav-item'
4052             
4053         }
4054         if (this.active) {
4055             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4056         }
4057         if (this.disabled) {
4058             cfg.cls += ' disabled';
4059         }
4060         
4061         if (this.href || this.html || this.glyphicon || this.icon) {
4062             cfg.cn = [
4063                 {
4064                     tag: this.tagtype,
4065                     href : this.href || "#",
4066                     html: this.html || ''
4067                 }
4068             ];
4069             
4070             if (this.icon) {
4071                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4072             }
4073
4074             if(this.glyphicon) {
4075                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4076             }
4077             
4078             if (this.menu) {
4079                 
4080                 cfg.cn[0].html += " <span class='caret'></span>";
4081              
4082             }
4083             
4084             if (this.badge !== '') {
4085                  
4086                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4087             }
4088         }
4089         
4090         
4091         
4092         return cfg;
4093     },
4094     initEvents: function() 
4095     {
4096         if (typeof (this.menu) != 'undefined') {
4097             this.menu.parentType = this.xtype;
4098             this.menu.triggerEl = this.el;
4099             this.menu = this.addxtype(Roo.apply({}, this.menu));
4100         }
4101         
4102         this.el.select('a',true).on('click', this.onClick, this);
4103         
4104         if(this.tagtype == 'span'){
4105             this.el.select('span',true).on('click', this.onClick, this);
4106         }
4107        
4108         // at this point parent should be available..
4109         this.parent().register(this);
4110     },
4111     
4112     onClick : function(e)
4113     {
4114         if(
4115                 this.preventDefault || 
4116                 this.href == '#' 
4117         ){
4118             
4119             e.preventDefault();
4120         }
4121         
4122         if (this.disabled) {
4123             return;
4124         }
4125         
4126         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4127         if (tg && tg.transition) {
4128             Roo.log("waiting for the transitionend");
4129             return;
4130         }
4131         
4132         
4133         
4134         //Roo.log("fire event clicked");
4135         if(this.fireEvent('click', this, e) === false){
4136             return;
4137         };
4138         
4139         if(this.tagtype == 'span'){
4140             return;
4141         }
4142         
4143         //Roo.log(this.href);
4144         var ael = this.el.select('a',true).first();
4145         //Roo.log(ael);
4146         
4147         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4148             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4149             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4150                 return; // ignore... - it's a 'hash' to another page.
4151             }
4152             
4153             e.preventDefault();
4154             this.scrollToElement(e);
4155         }
4156         
4157         
4158         var p =  this.parent();
4159    
4160         if (['tabs','pills'].indexOf(p.type)!==-1) {
4161             if (typeof(p.setActiveItem) !== 'undefined') {
4162                 p.setActiveItem(this);
4163             }
4164         }
4165         
4166         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4167         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4168             // remove the collapsed menu expand...
4169             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4170         }
4171     },
4172     
4173     isActive: function () {
4174         return this.active
4175     },
4176     setActive : function(state, fire, is_was_active)
4177     {
4178         if (this.active && !state & this.navId) {
4179             this.was_active = true;
4180             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4181             if (nv) {
4182                 nv.clearWasActive(this);
4183             }
4184             
4185         }
4186         this.active = state;
4187         
4188         if (!state ) {
4189             this.el.removeClass('active');
4190         } else if (!this.el.hasClass('active')) {
4191             this.el.addClass('active');
4192         }
4193         if (fire) {
4194             this.fireEvent('changed', this, state);
4195         }
4196         
4197         // show a panel if it's registered and related..
4198         
4199         if (!this.navId || !this.tabId || !state || is_was_active) {
4200             return;
4201         }
4202         
4203         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4204         if (!tg) {
4205             return;
4206         }
4207         var pan = tg.getPanelByName(this.tabId);
4208         if (!pan) {
4209             return;
4210         }
4211         // if we can not flip to new panel - go back to old nav highlight..
4212         if (false == tg.showPanel(pan)) {
4213             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4214             if (nv) {
4215                 var onav = nv.getWasActive();
4216                 if (onav) {
4217                     onav.setActive(true, false, true);
4218                 }
4219             }
4220             
4221         }
4222         
4223         
4224         
4225     },
4226      // this should not be here...
4227     setDisabled : function(state)
4228     {
4229         this.disabled = state;
4230         if (!state ) {
4231             this.el.removeClass('disabled');
4232         } else if (!this.el.hasClass('disabled')) {
4233             this.el.addClass('disabled');
4234         }
4235         
4236     },
4237     
4238     /**
4239      * Fetch the element to display the tooltip on.
4240      * @return {Roo.Element} defaults to this.el
4241      */
4242     tooltipEl : function()
4243     {
4244         return this.el.select('' + this.tagtype + '', true).first();
4245     },
4246     
4247     scrollToElement : function(e)
4248     {
4249         var c = document.body;
4250         
4251         /*
4252          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4253          */
4254         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4255             c = document.documentElement;
4256         }
4257         
4258         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4259         
4260         if(!target){
4261             return;
4262         }
4263
4264         var o = target.calcOffsetsTo(c);
4265         
4266         var options = {
4267             target : target,
4268             value : o[1]
4269         }
4270         
4271         this.fireEvent('scrollto', this, options, e);
4272         
4273         Roo.get(c).scrollTo('top', options.value, true);
4274         
4275         return;
4276     }
4277 });
4278  
4279
4280  /*
4281  * - LGPL
4282  *
4283  * sidebar item
4284  *
4285  *  li
4286  *    <span> icon </span>
4287  *    <span> text </span>
4288  *    <span>badge </span>
4289  */
4290
4291 /**
4292  * @class Roo.bootstrap.NavSidebarItem
4293  * @extends Roo.bootstrap.NavItem
4294  * Bootstrap Navbar.NavSidebarItem class
4295  * @constructor
4296  * Create a new Navbar Button
4297  * @param {Object} config The config object
4298  */
4299 Roo.bootstrap.NavSidebarItem = function(config){
4300     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4301     this.addEvents({
4302         // raw events
4303         /**
4304          * @event click
4305          * The raw click event for the entire grid.
4306          * @param {Roo.EventObject} e
4307          */
4308         "click" : true,
4309          /**
4310             * @event changed
4311             * Fires when the active item active state changes
4312             * @param {Roo.bootstrap.NavSidebarItem} this
4313             * @param {boolean} state the new state
4314              
4315          */
4316         'changed': true
4317     });
4318    
4319 };
4320
4321 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4322     
4323     
4324     getAutoCreate : function(){
4325         
4326         
4327         var a = {
4328                 tag: 'a',
4329                 href : this.href || '#',
4330                 cls: '',
4331                 html : '',
4332                 cn : []
4333         };
4334         var cfg = {
4335             tag: 'li',
4336             cls: '',
4337             cn: [ a ]
4338         }
4339         var span = {
4340             tag: 'span',
4341             html : this.html || ''
4342         }
4343         
4344         
4345         if (this.active) {
4346             cfg.cls += ' active';
4347         }
4348         
4349         // left icon..
4350         if (this.glyphicon || this.icon) {
4351             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4352             a.cn.push({ tag : 'i', cls : c }) ;
4353         }
4354         // html..
4355         a.cn.push(span);
4356         // then badge..
4357         if (this.badge !== '') {
4358             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4359         }
4360         // fi
4361         if (this.menu) {
4362             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4363             a.cls += 'dropdown-toggle treeview' ;
4364             
4365         }
4366         
4367         
4368         
4369         return cfg;
4370          
4371            
4372     }
4373    
4374      
4375  
4376 });
4377  
4378
4379  /*
4380  * - LGPL
4381  *
4382  * row
4383  * 
4384  */
4385
4386 /**
4387  * @class Roo.bootstrap.Row
4388  * @extends Roo.bootstrap.Component
4389  * Bootstrap Row class (contains columns...)
4390  * 
4391  * @constructor
4392  * Create a new Row
4393  * @param {Object} config The config object
4394  */
4395
4396 Roo.bootstrap.Row = function(config){
4397     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4398 };
4399
4400 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4401     
4402     getAutoCreate : function(){
4403        return {
4404             cls: 'row clearfix'
4405        };
4406     }
4407     
4408     
4409 });
4410
4411  
4412
4413  /*
4414  * - LGPL
4415  *
4416  * element
4417  * 
4418  */
4419
4420 /**
4421  * @class Roo.bootstrap.Element
4422  * @extends Roo.bootstrap.Component
4423  * Bootstrap Element class
4424  * @cfg {String} html contents of the element
4425  * @cfg {String} tag tag of the element
4426  * @cfg {String} cls class of the element
4427  * @cfg {Boolean} preventDefault (true|false) default false
4428  * @cfg {Boolean} clickable (true|false) default false
4429  * 
4430  * @constructor
4431  * Create a new Element
4432  * @param {Object} config The config object
4433  */
4434
4435 Roo.bootstrap.Element = function(config){
4436     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4437     
4438     this.addEvents({
4439         // raw events
4440         /**
4441          * @event click
4442          * When a element is chick
4443          * @param {Roo.bootstrap.Element} this
4444          * @param {Roo.EventObject} e
4445          */
4446         "click" : true
4447     });
4448 };
4449
4450 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4451     
4452     tag: 'div',
4453     cls: '',
4454     html: '',
4455     preventDefault: false, 
4456     clickable: false,
4457     
4458     getAutoCreate : function(){
4459         
4460         var cfg = {
4461             tag: this.tag,
4462             cls: this.cls,
4463             html: this.html
4464         }
4465         
4466         return cfg;
4467     },
4468     
4469     initEvents: function() 
4470     {
4471         Roo.bootstrap.Element.superclass.initEvents.call(this);
4472         
4473         if(this.clickable){
4474             this.el.on('click', this.onClick, this);
4475         }
4476         
4477     },
4478     
4479     onClick : function(e)
4480     {
4481         if(this.preventDefault){
4482             e.preventDefault();
4483         }
4484         
4485         this.fireEvent('click', this, e);
4486     },
4487     
4488     getValue : function()
4489     {
4490         return this.el.dom.innerHTML;
4491     },
4492     
4493     setValue : function(value)
4494     {
4495         this.el.dom.innerHTML = value;
4496     }
4497    
4498 });
4499
4500  
4501
4502  /*
4503  * - LGPL
4504  *
4505  * pagination
4506  * 
4507  */
4508
4509 /**
4510  * @class Roo.bootstrap.Pagination
4511  * @extends Roo.bootstrap.Component
4512  * Bootstrap Pagination class
4513  * @cfg {String} size xs | sm | md | lg
4514  * @cfg {Boolean} inverse false | true
4515  * 
4516  * @constructor
4517  * Create a new Pagination
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Pagination = function(config){
4522     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4526     
4527     cls: false,
4528     size: false,
4529     inverse: false,
4530     
4531     getAutoCreate : function(){
4532         var cfg = {
4533             tag: 'ul',
4534                 cls: 'pagination'
4535         };
4536         if (this.inverse) {
4537             cfg.cls += ' inverse';
4538         }
4539         if (this.html) {
4540             cfg.html=this.html;
4541         }
4542         if (this.cls) {
4543             cfg.cls += " " + this.cls;
4544         }
4545         return cfg;
4546     }
4547    
4548 });
4549
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * Pagination item
4556  * 
4557  */
4558
4559
4560 /**
4561  * @class Roo.bootstrap.PaginationItem
4562  * @extends Roo.bootstrap.Component
4563  * Bootstrap PaginationItem class
4564  * @cfg {String} html text
4565  * @cfg {String} href the link
4566  * @cfg {Boolean} preventDefault (true | false) default true
4567  * @cfg {Boolean} active (true | false) default false
4568  * @cfg {Boolean} disabled default false
4569  * 
4570  * 
4571  * @constructor
4572  * Create a new PaginationItem
4573  * @param {Object} config The config object
4574  */
4575
4576
4577 Roo.bootstrap.PaginationItem = function(config){
4578     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true
4587     });
4588 };
4589
4590 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4591     
4592     href : false,
4593     html : false,
4594     preventDefault: true,
4595     active : false,
4596     cls : false,
4597     disabled: false,
4598     
4599     getAutoCreate : function(){
4600         var cfg= {
4601             tag: 'li',
4602             cn: [
4603                 {
4604                     tag : 'a',
4605                     href : this.href ? this.href : '#',
4606                     html : this.html ? this.html : ''
4607                 }
4608             ]
4609         };
4610         
4611         if(this.cls){
4612             cfg.cls = this.cls;
4613         }
4614         
4615         if(this.disabled){
4616             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4617         }
4618         
4619         if(this.active){
4620             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4621         }
4622         
4623         return cfg;
4624     },
4625     
4626     initEvents: function() {
4627         
4628         this.el.on('click', this.onClick, this);
4629         
4630     },
4631     onClick : function(e)
4632     {
4633         Roo.log('PaginationItem on click ');
4634         if(this.preventDefault){
4635             e.preventDefault();
4636         }
4637         
4638         if(this.disabled){
4639             return;
4640         }
4641         
4642         this.fireEvent('click', this, e);
4643     }
4644    
4645 });
4646
4647  
4648
4649  /*
4650  * - LGPL
4651  *
4652  * slider
4653  * 
4654  */
4655
4656
4657 /**
4658  * @class Roo.bootstrap.Slider
4659  * @extends Roo.bootstrap.Component
4660  * Bootstrap Slider class
4661  *    
4662  * @constructor
4663  * Create a new Slider
4664  * @param {Object} config The config object
4665  */
4666
4667 Roo.bootstrap.Slider = function(config){
4668     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4669 };
4670
4671 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4672     
4673     getAutoCreate : function(){
4674         
4675         var cfg = {
4676             tag: 'div',
4677             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4678             cn: [
4679                 {
4680                     tag: 'a',
4681                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4682                 }
4683             ]
4684         }
4685         
4686         return cfg;
4687     }
4688    
4689 });
4690
4691  /*
4692  * Based on:
4693  * Ext JS Library 1.1.1
4694  * Copyright(c) 2006-2007, Ext JS, LLC.
4695  *
4696  * Originally Released Under LGPL - original licence link has changed is not relivant.
4697  *
4698  * Fork - LGPL
4699  * <script type="text/javascript">
4700  */
4701  
4702
4703 /**
4704  * @class Roo.grid.ColumnModel
4705  * @extends Roo.util.Observable
4706  * This is the default implementation of a ColumnModel used by the Grid. It defines
4707  * the columns in the grid.
4708  * <br>Usage:<br>
4709  <pre><code>
4710  var colModel = new Roo.grid.ColumnModel([
4711         {header: "Ticker", width: 60, sortable: true, locked: true},
4712         {header: "Company Name", width: 150, sortable: true},
4713         {header: "Market Cap.", width: 100, sortable: true},
4714         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4715         {header: "Employees", width: 100, sortable: true, resizable: false}
4716  ]);
4717  </code></pre>
4718  * <p>
4719  
4720  * The config options listed for this class are options which may appear in each
4721  * individual column definition.
4722  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4723  * @constructor
4724  * @param {Object} config An Array of column config objects. See this class's
4725  * config objects for details.
4726 */
4727 Roo.grid.ColumnModel = function(config){
4728         /**
4729      * The config passed into the constructor
4730      */
4731     this.config = config;
4732     this.lookup = {};
4733
4734     // if no id, create one
4735     // if the column does not have a dataIndex mapping,
4736     // map it to the order it is in the config
4737     for(var i = 0, len = config.length; i < len; i++){
4738         var c = config[i];
4739         if(typeof c.dataIndex == "undefined"){
4740             c.dataIndex = i;
4741         }
4742         if(typeof c.renderer == "string"){
4743             c.renderer = Roo.util.Format[c.renderer];
4744         }
4745         if(typeof c.id == "undefined"){
4746             c.id = Roo.id();
4747         }
4748         if(c.editor && c.editor.xtype){
4749             c.editor  = Roo.factory(c.editor, Roo.grid);
4750         }
4751         if(c.editor && c.editor.isFormField){
4752             c.editor = new Roo.grid.GridEditor(c.editor);
4753         }
4754         this.lookup[c.id] = c;
4755     }
4756
4757     /**
4758      * The width of columns which have no width specified (defaults to 100)
4759      * @type Number
4760      */
4761     this.defaultWidth = 100;
4762
4763     /**
4764      * Default sortable of columns which have no sortable specified (defaults to false)
4765      * @type Boolean
4766      */
4767     this.defaultSortable = false;
4768
4769     this.addEvents({
4770         /**
4771              * @event widthchange
4772              * Fires when the width of a column changes.
4773              * @param {ColumnModel} this
4774              * @param {Number} columnIndex The column index
4775              * @param {Number} newWidth The new width
4776              */
4777             "widthchange": true,
4778         /**
4779              * @event headerchange
4780              * Fires when the text of a header changes.
4781              * @param {ColumnModel} this
4782              * @param {Number} columnIndex The column index
4783              * @param {Number} newText The new header text
4784              */
4785             "headerchange": true,
4786         /**
4787              * @event hiddenchange
4788              * Fires when a column is hidden or "unhidden".
4789              * @param {ColumnModel} this
4790              * @param {Number} columnIndex The column index
4791              * @param {Boolean} hidden true if hidden, false otherwise
4792              */
4793             "hiddenchange": true,
4794             /**
4795          * @event columnmoved
4796          * Fires when a column is moved.
4797          * @param {ColumnModel} this
4798          * @param {Number} oldIndex
4799          * @param {Number} newIndex
4800          */
4801         "columnmoved" : true,
4802         /**
4803          * @event columlockchange
4804          * Fires when a column's locked state is changed
4805          * @param {ColumnModel} this
4806          * @param {Number} colIndex
4807          * @param {Boolean} locked true if locked
4808          */
4809         "columnlockchange" : true
4810     });
4811     Roo.grid.ColumnModel.superclass.constructor.call(this);
4812 };
4813 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4814     /**
4815      * @cfg {String} header The header text to display in the Grid view.
4816      */
4817     /**
4818      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4819      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4820      * specified, the column's index is used as an index into the Record's data Array.
4821      */
4822     /**
4823      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4824      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4825      */
4826     /**
4827      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4828      * Defaults to the value of the {@link #defaultSortable} property.
4829      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4830      */
4831     /**
4832      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4833      */
4834     /**
4835      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4836      */
4837     /**
4838      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4839      */
4840     /**
4841      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4842      */
4843     /**
4844      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4845      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4846      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4847      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4848      */
4849        /**
4850      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4851      */
4852     /**
4853      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4854      */
4855     /**
4856      * @cfg {String} cursor (Optional)
4857      */
4858     /**
4859      * @cfg {String} tooltip (Optional)
4860      */
4861     /**
4862      * Returns the id of the column at the specified index.
4863      * @param {Number} index The column index
4864      * @return {String} the id
4865      */
4866     getColumnId : function(index){
4867         return this.config[index].id;
4868     },
4869
4870     /**
4871      * Returns the column for a specified id.
4872      * @param {String} id The column id
4873      * @return {Object} the column
4874      */
4875     getColumnById : function(id){
4876         return this.lookup[id];
4877     },
4878
4879     
4880     /**
4881      * Returns the column for a specified dataIndex.
4882      * @param {String} dataIndex The column dataIndex
4883      * @return {Object|Boolean} the column or false if not found
4884      */
4885     getColumnByDataIndex: function(dataIndex){
4886         var index = this.findColumnIndex(dataIndex);
4887         return index > -1 ? this.config[index] : false;
4888     },
4889     
4890     /**
4891      * Returns the index for a specified column id.
4892      * @param {String} id The column id
4893      * @return {Number} the index, or -1 if not found
4894      */
4895     getIndexById : function(id){
4896         for(var i = 0, len = this.config.length; i < len; i++){
4897             if(this.config[i].id == id){
4898                 return i;
4899             }
4900         }
4901         return -1;
4902     },
4903     
4904     /**
4905      * Returns the index for a specified column dataIndex.
4906      * @param {String} dataIndex The column dataIndex
4907      * @return {Number} the index, or -1 if not found
4908      */
4909     
4910     findColumnIndex : function(dataIndex){
4911         for(var i = 0, len = this.config.length; i < len; i++){
4912             if(this.config[i].dataIndex == dataIndex){
4913                 return i;
4914             }
4915         }
4916         return -1;
4917     },
4918     
4919     
4920     moveColumn : function(oldIndex, newIndex){
4921         var c = this.config[oldIndex];
4922         this.config.splice(oldIndex, 1);
4923         this.config.splice(newIndex, 0, c);
4924         this.dataMap = null;
4925         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4926     },
4927
4928     isLocked : function(colIndex){
4929         return this.config[colIndex].locked === true;
4930     },
4931
4932     setLocked : function(colIndex, value, suppressEvent){
4933         if(this.isLocked(colIndex) == value){
4934             return;
4935         }
4936         this.config[colIndex].locked = value;
4937         if(!suppressEvent){
4938             this.fireEvent("columnlockchange", this, colIndex, value);
4939         }
4940     },
4941
4942     getTotalLockedWidth : function(){
4943         var totalWidth = 0;
4944         for(var i = 0; i < this.config.length; i++){
4945             if(this.isLocked(i) && !this.isHidden(i)){
4946                 this.totalWidth += this.getColumnWidth(i);
4947             }
4948         }
4949         return totalWidth;
4950     },
4951
4952     getLockedCount : function(){
4953         for(var i = 0, len = this.config.length; i < len; i++){
4954             if(!this.isLocked(i)){
4955                 return i;
4956             }
4957         }
4958     },
4959
4960     /**
4961      * Returns the number of columns.
4962      * @return {Number}
4963      */
4964     getColumnCount : function(visibleOnly){
4965         if(visibleOnly === true){
4966             var c = 0;
4967             for(var i = 0, len = this.config.length; i < len; i++){
4968                 if(!this.isHidden(i)){
4969                     c++;
4970                 }
4971             }
4972             return c;
4973         }
4974         return this.config.length;
4975     },
4976
4977     /**
4978      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4979      * @param {Function} fn
4980      * @param {Object} scope (optional)
4981      * @return {Array} result
4982      */
4983     getColumnsBy : function(fn, scope){
4984         var r = [];
4985         for(var i = 0, len = this.config.length; i < len; i++){
4986             var c = this.config[i];
4987             if(fn.call(scope||this, c, i) === true){
4988                 r[r.length] = c;
4989             }
4990         }
4991         return r;
4992     },
4993
4994     /**
4995      * Returns true if the specified column is sortable.
4996      * @param {Number} col The column index
4997      * @return {Boolean}
4998      */
4999     isSortable : function(col){
5000         if(typeof this.config[col].sortable == "undefined"){
5001             return this.defaultSortable;
5002         }
5003         return this.config[col].sortable;
5004     },
5005
5006     /**
5007      * Returns the rendering (formatting) function defined for the column.
5008      * @param {Number} col The column index.
5009      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5010      */
5011     getRenderer : function(col){
5012         if(!this.config[col].renderer){
5013             return Roo.grid.ColumnModel.defaultRenderer;
5014         }
5015         return this.config[col].renderer;
5016     },
5017
5018     /**
5019      * Sets the rendering (formatting) function for a column.
5020      * @param {Number} col The column index
5021      * @param {Function} fn The function to use to process the cell's raw data
5022      * to return HTML markup for the grid view. The render function is called with
5023      * the following parameters:<ul>
5024      * <li>Data value.</li>
5025      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5026      * <li>css A CSS style string to apply to the table cell.</li>
5027      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5028      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5029      * <li>Row index</li>
5030      * <li>Column index</li>
5031      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5032      */
5033     setRenderer : function(col, fn){
5034         this.config[col].renderer = fn;
5035     },
5036
5037     /**
5038      * Returns the width for the specified column.
5039      * @param {Number} col The column index
5040      * @return {Number}
5041      */
5042     getColumnWidth : function(col){
5043         return this.config[col].width * 1 || this.defaultWidth;
5044     },
5045
5046     /**
5047      * Sets the width for a column.
5048      * @param {Number} col The column index
5049      * @param {Number} width The new width
5050      */
5051     setColumnWidth : function(col, width, suppressEvent){
5052         this.config[col].width = width;
5053         this.totalWidth = null;
5054         if(!suppressEvent){
5055              this.fireEvent("widthchange", this, col, width);
5056         }
5057     },
5058
5059     /**
5060      * Returns the total width of all columns.
5061      * @param {Boolean} includeHidden True to include hidden column widths
5062      * @return {Number}
5063      */
5064     getTotalWidth : function(includeHidden){
5065         if(!this.totalWidth){
5066             this.totalWidth = 0;
5067             for(var i = 0, len = this.config.length; i < len; i++){
5068                 if(includeHidden || !this.isHidden(i)){
5069                     this.totalWidth += this.getColumnWidth(i);
5070                 }
5071             }
5072         }
5073         return this.totalWidth;
5074     },
5075
5076     /**
5077      * Returns the header for the specified column.
5078      * @param {Number} col The column index
5079      * @return {String}
5080      */
5081     getColumnHeader : function(col){
5082         return this.config[col].header;
5083     },
5084
5085     /**
5086      * Sets the header for a column.
5087      * @param {Number} col The column index
5088      * @param {String} header The new header
5089      */
5090     setColumnHeader : function(col, header){
5091         this.config[col].header = header;
5092         this.fireEvent("headerchange", this, col, header);
5093     },
5094
5095     /**
5096      * Returns the tooltip for the specified column.
5097      * @param {Number} col The column index
5098      * @return {String}
5099      */
5100     getColumnTooltip : function(col){
5101             return this.config[col].tooltip;
5102     },
5103     /**
5104      * Sets the tooltip for a column.
5105      * @param {Number} col The column index
5106      * @param {String} tooltip The new tooltip
5107      */
5108     setColumnTooltip : function(col, tooltip){
5109             this.config[col].tooltip = tooltip;
5110     },
5111
5112     /**
5113      * Returns the dataIndex for the specified column.
5114      * @param {Number} col The column index
5115      * @return {Number}
5116      */
5117     getDataIndex : function(col){
5118         return this.config[col].dataIndex;
5119     },
5120
5121     /**
5122      * Sets the dataIndex for a column.
5123      * @param {Number} col The column index
5124      * @param {Number} dataIndex The new dataIndex
5125      */
5126     setDataIndex : function(col, dataIndex){
5127         this.config[col].dataIndex = dataIndex;
5128     },
5129
5130     
5131     
5132     /**
5133      * Returns true if the cell is editable.
5134      * @param {Number} colIndex The column index
5135      * @param {Number} rowIndex The row index
5136      * @return {Boolean}
5137      */
5138     isCellEditable : function(colIndex, rowIndex){
5139         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5140     },
5141
5142     /**
5143      * Returns the editor defined for the cell/column.
5144      * return false or null to disable editing.
5145      * @param {Number} colIndex The column index
5146      * @param {Number} rowIndex The row index
5147      * @return {Object}
5148      */
5149     getCellEditor : function(colIndex, rowIndex){
5150         return this.config[colIndex].editor;
5151     },
5152
5153     /**
5154      * Sets if a column is editable.
5155      * @param {Number} col The column index
5156      * @param {Boolean} editable True if the column is editable
5157      */
5158     setEditable : function(col, editable){
5159         this.config[col].editable = editable;
5160     },
5161
5162
5163     /**
5164      * Returns true if the column is hidden.
5165      * @param {Number} colIndex The column index
5166      * @return {Boolean}
5167      */
5168     isHidden : function(colIndex){
5169         return this.config[colIndex].hidden;
5170     },
5171
5172
5173     /**
5174      * Returns true if the column width cannot be changed
5175      */
5176     isFixed : function(colIndex){
5177         return this.config[colIndex].fixed;
5178     },
5179
5180     /**
5181      * Returns true if the column can be resized
5182      * @return {Boolean}
5183      */
5184     isResizable : function(colIndex){
5185         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5186     },
5187     /**
5188      * Sets if a column is hidden.
5189      * @param {Number} colIndex The column index
5190      * @param {Boolean} hidden True if the column is hidden
5191      */
5192     setHidden : function(colIndex, hidden){
5193         this.config[colIndex].hidden = hidden;
5194         this.totalWidth = null;
5195         this.fireEvent("hiddenchange", this, colIndex, hidden);
5196     },
5197
5198     /**
5199      * Sets the editor for a column.
5200      * @param {Number} col The column index
5201      * @param {Object} editor The editor object
5202      */
5203     setEditor : function(col, editor){
5204         this.config[col].editor = editor;
5205     }
5206 });
5207
5208 Roo.grid.ColumnModel.defaultRenderer = function(value){
5209         if(typeof value == "string" && value.length < 1){
5210             return "&#160;";
5211         }
5212         return value;
5213 };
5214
5215 // Alias for backwards compatibility
5216 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5217 /*
5218  * Based on:
5219  * Ext JS Library 1.1.1
5220  * Copyright(c) 2006-2007, Ext JS, LLC.
5221  *
5222  * Originally Released Under LGPL - original licence link has changed is not relivant.
5223  *
5224  * Fork - LGPL
5225  * <script type="text/javascript">
5226  */
5227  
5228 /**
5229  * @class Roo.LoadMask
5230  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5231  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5232  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5233  * element's UpdateManager load indicator and will be destroyed after the initial load.
5234  * @constructor
5235  * Create a new LoadMask
5236  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5237  * @param {Object} config The config object
5238  */
5239 Roo.LoadMask = function(el, config){
5240     this.el = Roo.get(el);
5241     Roo.apply(this, config);
5242     if(this.store){
5243         this.store.on('beforeload', this.onBeforeLoad, this);
5244         this.store.on('load', this.onLoad, this);
5245         this.store.on('loadexception', this.onLoadException, this);
5246         this.removeMask = false;
5247     }else{
5248         var um = this.el.getUpdateManager();
5249         um.showLoadIndicator = false; // disable the default indicator
5250         um.on('beforeupdate', this.onBeforeLoad, this);
5251         um.on('update', this.onLoad, this);
5252         um.on('failure', this.onLoad, this);
5253         this.removeMask = true;
5254     }
5255 };
5256
5257 Roo.LoadMask.prototype = {
5258     /**
5259      * @cfg {Boolean} removeMask
5260      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5261      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5262      */
5263     /**
5264      * @cfg {String} msg
5265      * The text to display in a centered loading message box (defaults to 'Loading...')
5266      */
5267     msg : 'Loading...',
5268     /**
5269      * @cfg {String} msgCls
5270      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5271      */
5272     msgCls : 'x-mask-loading',
5273
5274     /**
5275      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5276      * @type Boolean
5277      */
5278     disabled: false,
5279
5280     /**
5281      * Disables the mask to prevent it from being displayed
5282      */
5283     disable : function(){
5284        this.disabled = true;
5285     },
5286
5287     /**
5288      * Enables the mask so that it can be displayed
5289      */
5290     enable : function(){
5291         this.disabled = false;
5292     },
5293     
5294     onLoadException : function()
5295     {
5296         Roo.log(arguments);
5297         
5298         if (typeof(arguments[3]) != 'undefined') {
5299             Roo.MessageBox.alert("Error loading",arguments[3]);
5300         } 
5301         /*
5302         try {
5303             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5304                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5305             }   
5306         } catch(e) {
5307             
5308         }
5309         */
5310     
5311         
5312         
5313         this.el.unmask(this.removeMask);
5314     },
5315     // private
5316     onLoad : function()
5317     {
5318         this.el.unmask(this.removeMask);
5319     },
5320
5321     // private
5322     onBeforeLoad : function(){
5323         if(!this.disabled){
5324             this.el.mask(this.msg, this.msgCls);
5325         }
5326     },
5327
5328     // private
5329     destroy : function(){
5330         if(this.store){
5331             this.store.un('beforeload', this.onBeforeLoad, this);
5332             this.store.un('load', this.onLoad, this);
5333             this.store.un('loadexception', this.onLoadException, this);
5334         }else{
5335             var um = this.el.getUpdateManager();
5336             um.un('beforeupdate', this.onBeforeLoad, this);
5337             um.un('update', this.onLoad, this);
5338             um.un('failure', this.onLoad, this);
5339         }
5340     }
5341 };/*
5342  * - LGPL
5343  *
5344  * table
5345  * 
5346  */
5347
5348 /**
5349  * @class Roo.bootstrap.Table
5350  * @extends Roo.bootstrap.Component
5351  * Bootstrap Table class
5352  * @cfg {String} cls table class
5353  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5354  * @cfg {String} bgcolor Specifies the background color for a table
5355  * @cfg {Number} border Specifies whether the table cells should have borders or not
5356  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5357  * @cfg {Number} cellspacing Specifies the space between cells
5358  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5359  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5360  * @cfg {String} sortable Specifies that the table should be sortable
5361  * @cfg {String} summary Specifies a summary of the content of a table
5362  * @cfg {Number} width Specifies the width of a table
5363  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5364  * 
5365  * @cfg {boolean} striped Should the rows be alternative striped
5366  * @cfg {boolean} bordered Add borders to the table
5367  * @cfg {boolean} hover Add hover highlighting
5368  * @cfg {boolean} condensed Format condensed
5369  * @cfg {boolean} responsive Format condensed
5370  * @cfg {Boolean} loadMask (true|false) default false
5371  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5372  * @cfg {Boolean} thead (true|false) generate thead, default true
5373  * @cfg {Boolean} RowSelection (true|false) default false
5374  * @cfg {Boolean} CellSelection (true|false) default false
5375  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5376  
5377  * 
5378  * @constructor
5379  * Create a new Table
5380  * @param {Object} config The config object
5381  */
5382
5383 Roo.bootstrap.Table = function(config){
5384     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5385     
5386     if (this.sm) {
5387         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5388         this.sm = this.selModel;
5389         this.sm.xmodule = this.xmodule || false;
5390     }
5391     if (this.cm && typeof(this.cm.config) == 'undefined') {
5392         this.colModel = new Roo.grid.ColumnModel(this.cm);
5393         this.cm = this.colModel;
5394         this.cm.xmodule = this.xmodule || false;
5395     }
5396     if (this.store) {
5397         this.store= Roo.factory(this.store, Roo.data);
5398         this.ds = this.store;
5399         this.ds.xmodule = this.xmodule || false;
5400          
5401     }
5402     if (this.footer && this.store) {
5403         this.footer.dataSource = this.ds;
5404         this.footer = Roo.factory(this.footer);
5405     }
5406     
5407     /** @private */
5408     this.addEvents({
5409         /**
5410          * @event cellclick
5411          * Fires when a cell is clicked
5412          * @param {Roo.bootstrap.Table} this
5413          * @param {Roo.Element} el
5414          * @param {Number} rowIndex
5415          * @param {Number} columnIndex
5416          * @param {Roo.EventObject} e
5417          */
5418         "cellclick" : true,
5419         /**
5420          * @event celldblclick
5421          * Fires when a cell is double clicked
5422          * @param {Roo.bootstrap.Table} this
5423          * @param {Roo.Element} el
5424          * @param {Number} rowIndex
5425          * @param {Number} columnIndex
5426          * @param {Roo.EventObject} e
5427          */
5428         "celldblclick" : true,
5429         /**
5430          * @event rowclick
5431          * Fires when a row is clicked
5432          * @param {Roo.bootstrap.Table} this
5433          * @param {Roo.Element} el
5434          * @param {Number} rowIndex
5435          * @param {Roo.EventObject} e
5436          */
5437         "rowclick" : true,
5438         /**
5439          * @event rowdblclick
5440          * Fires when a row is double clicked
5441          * @param {Roo.bootstrap.Table} this
5442          * @param {Roo.Element} el
5443          * @param {Number} rowIndex
5444          * @param {Roo.EventObject} e
5445          */
5446         "rowdblclick" : true,
5447         /**
5448          * @event mouseover
5449          * Fires when a mouseover occur
5450          * @param {Roo.bootstrap.Table} this
5451          * @param {Roo.Element} el
5452          * @param {Number} rowIndex
5453          * @param {Number} columnIndex
5454          * @param {Roo.EventObject} e
5455          */
5456         "mouseover" : true,
5457         /**
5458          * @event mouseout
5459          * Fires when a mouseout occur
5460          * @param {Roo.bootstrap.Table} this
5461          * @param {Roo.Element} el
5462          * @param {Number} rowIndex
5463          * @param {Number} columnIndex
5464          * @param {Roo.EventObject} e
5465          */
5466         "mouseout" : true,
5467         /**
5468          * @event rowclass
5469          * Fires when a row is rendered, so you can change add a style to it.
5470          * @param {Roo.bootstrap.Table} this
5471          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5472          */
5473         'rowclass' : true,
5474           /**
5475          * @event rowsrendered
5476          * Fires when all the  rows have been rendered
5477          * @param {Roo.bootstrap.Table} this
5478          */
5479         'rowsrendered' : true
5480         
5481     });
5482 };
5483
5484 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5485     
5486     cls: false,
5487     align: false,
5488     bgcolor: false,
5489     border: false,
5490     cellpadding: false,
5491     cellspacing: false,
5492     frame: false,
5493     rules: false,
5494     sortable: false,
5495     summary: false,
5496     width: false,
5497     striped : false,
5498     bordered: false,
5499     hover:  false,
5500     condensed : false,
5501     responsive : false,
5502     sm : false,
5503     cm : false,
5504     store : false,
5505     loadMask : false,
5506     tfoot : true,
5507     thead : true,
5508     RowSelection : false,
5509     CellSelection : false,
5510     layout : false,
5511     
5512     // Roo.Element - the tbody
5513     mainBody: false, 
5514     
5515     getAutoCreate : function(){
5516         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5517         
5518         cfg = {
5519             tag: 'table',
5520             cls : 'table',
5521             cn : []
5522         }
5523             
5524         if (this.striped) {
5525             cfg.cls += ' table-striped';
5526         }
5527         
5528         if (this.hover) {
5529             cfg.cls += ' table-hover';
5530         }
5531         if (this.bordered) {
5532             cfg.cls += ' table-bordered';
5533         }
5534         if (this.condensed) {
5535             cfg.cls += ' table-condensed';
5536         }
5537         if (this.responsive) {
5538             cfg.cls += ' table-responsive';
5539         }
5540         
5541         if (this.cls) {
5542             cfg.cls+=  ' ' +this.cls;
5543         }
5544         
5545         // this lot should be simplifed...
5546         
5547         if (this.align) {
5548             cfg.align=this.align;
5549         }
5550         if (this.bgcolor) {
5551             cfg.bgcolor=this.bgcolor;
5552         }
5553         if (this.border) {
5554             cfg.border=this.border;
5555         }
5556         if (this.cellpadding) {
5557             cfg.cellpadding=this.cellpadding;
5558         }
5559         if (this.cellspacing) {
5560             cfg.cellspacing=this.cellspacing;
5561         }
5562         if (this.frame) {
5563             cfg.frame=this.frame;
5564         }
5565         if (this.rules) {
5566             cfg.rules=this.rules;
5567         }
5568         if (this.sortable) {
5569             cfg.sortable=this.sortable;
5570         }
5571         if (this.summary) {
5572             cfg.summary=this.summary;
5573         }
5574         if (this.width) {
5575             cfg.width=this.width;
5576         }
5577         if (this.layout) {
5578             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5579         }
5580         
5581         if(this.store || this.cm){
5582             if(this.thead){
5583                 cfg.cn.push(this.renderHeader());
5584             }
5585             
5586             cfg.cn.push(this.renderBody());
5587             
5588             if(this.tfoot){
5589                 cfg.cn.push(this.renderFooter());
5590             }
5591             
5592             cfg.cls+=  ' TableGrid';
5593         }
5594         
5595         return { cn : [ cfg ] };
5596     },
5597     
5598     initEvents : function()
5599     {   
5600         if(!this.store || !this.cm){
5601             return;
5602         }
5603         
5604         //Roo.log('initEvents with ds!!!!');
5605         
5606         this.mainBody = this.el.select('tbody', true).first();
5607         
5608         
5609         var _this = this;
5610         
5611         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5612             e.on('click', _this.sort, _this);
5613         });
5614         
5615         this.el.on("click", this.onClick, this);
5616         this.el.on("dblclick", this.onDblClick, this);
5617         
5618         // why is this done????? = it breaks dialogs??
5619         //this.parent().el.setStyle('position', 'relative');
5620         
5621         
5622         if (this.footer) {
5623             this.footer.parentId = this.id;
5624             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5625         }
5626         
5627         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5628         
5629         this.store.on('load', this.onLoad, this);
5630         this.store.on('beforeload', this.onBeforeLoad, this);
5631         this.store.on('update', this.onUpdate, this);
5632         this.store.on('add', this.onAdd, this);
5633         
5634     },
5635     
5636     onMouseover : function(e, el)
5637     {
5638         var cell = Roo.get(el);
5639         
5640         if(!cell){
5641             return;
5642         }
5643         
5644         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5645             cell = cell.findParent('td', false, true);
5646         }
5647         
5648         var row = cell.findParent('tr', false, true);
5649         var cellIndex = cell.dom.cellIndex;
5650         var rowIndex = row.dom.rowIndex - 1; // start from 0
5651         
5652         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5653         
5654     },
5655     
5656     onMouseout : function(e, el)
5657     {
5658         var cell = Roo.get(el);
5659         
5660         if(!cell){
5661             return;
5662         }
5663         
5664         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5665             cell = cell.findParent('td', false, true);
5666         }
5667         
5668         var row = cell.findParent('tr', false, true);
5669         var cellIndex = cell.dom.cellIndex;
5670         var rowIndex = row.dom.rowIndex - 1; // start from 0
5671         
5672         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5673         
5674     },
5675     
5676     onClick : function(e, el)
5677     {
5678         var cell = Roo.get(el);
5679         
5680         if(!cell || (!this.CellSelection && !this.RowSelection)){
5681             return;
5682         }
5683         
5684         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5685             cell = cell.findParent('td', false, true);
5686         }
5687         
5688         if(!cell || typeof(cell) == 'undefined'){
5689             return;
5690         }
5691         
5692         var row = cell.findParent('tr', false, true);
5693         
5694         if(!row || typeof(row) == 'undefined'){
5695             return;
5696         }
5697         
5698         var cellIndex = cell.dom.cellIndex;
5699         var rowIndex = this.getRowIndex(row);
5700         
5701         if(this.CellSelection){
5702             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5703         }
5704         
5705         if(this.RowSelection){
5706             this.fireEvent('rowclick', this, row, rowIndex, e);
5707         }
5708         
5709         
5710     },
5711     
5712     onDblClick : function(e,el)
5713     {
5714         var cell = Roo.get(el);
5715         
5716         if(!cell || (!this.CellSelection && !this.RowSelection)){
5717             return;
5718         }
5719         
5720         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5721             cell = cell.findParent('td', false, true);
5722         }
5723         
5724         if(!cell || typeof(cell) == 'undefined'){
5725             return;
5726         }
5727         
5728         var row = cell.findParent('tr', false, true);
5729         
5730         if(!row || typeof(row) == 'undefined'){
5731             return;
5732         }
5733         
5734         var cellIndex = cell.dom.cellIndex;
5735         var rowIndex = this.getRowIndex(row);
5736         
5737         if(this.CellSelection){
5738             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5739         }
5740         
5741         if(this.RowSelection){
5742             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5743         }
5744     },
5745     
5746     sort : function(e,el)
5747     {
5748         var col = Roo.get(el);
5749         
5750         if(!col.hasClass('sortable')){
5751             return;
5752         }
5753         
5754         var sort = col.attr('sort');
5755         var dir = 'ASC';
5756         
5757         if(col.hasClass('glyphicon-arrow-up')){
5758             dir = 'DESC';
5759         }
5760         
5761         this.store.sortInfo = {field : sort, direction : dir};
5762         
5763         if (this.footer) {
5764             Roo.log("calling footer first");
5765             this.footer.onClick('first');
5766         } else {
5767         
5768             this.store.load({ params : { start : 0 } });
5769         }
5770     },
5771     
5772     renderHeader : function()
5773     {
5774         var header = {
5775             tag: 'thead',
5776             cn : []
5777         };
5778         
5779         var cm = this.cm;
5780         
5781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5782             
5783             var config = cm.config[i];
5784                     
5785             var c = {
5786                 tag: 'th',
5787                 style : '',
5788                 html: cm.getColumnHeader(i)
5789             };
5790             
5791             if(typeof(config.tooltip) != 'undefined'){
5792                 c.tooltip = config.tooltip;
5793             }
5794             
5795             if(typeof(config.colspan) != 'undefined'){
5796                 c.colspan = config.colspan;
5797             }
5798             
5799             if(typeof(config.hidden) != 'undefined' && config.hidden){
5800                 c.style += ' display:none;';
5801             }
5802             
5803             if(typeof(config.dataIndex) != 'undefined'){
5804                 c.sort = config.dataIndex;
5805             }
5806             
5807             if(typeof(config.sortable) != 'undefined' && config.sortable){
5808                 c.cls = 'sortable';
5809             }
5810             
5811             if(typeof(config.align) != 'undefined' && config.align.length){
5812                 c.style += ' text-align:' + config.align + ';';
5813             }
5814             
5815             if(typeof(config.width) != 'undefined'){
5816                 c.style += ' width:' + config.width + 'px;';
5817             }
5818             
5819             if(typeof(config.cls) != 'undefined'){
5820                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5821             }
5822             
5823             header.cn.push(c)
5824         }
5825         
5826         return header;
5827     },
5828     
5829     renderBody : function()
5830     {
5831         var body = {
5832             tag: 'tbody',
5833             cn : [
5834                 {
5835                     tag: 'tr',
5836                     cn : [
5837                         {
5838                             tag : 'td',
5839                             colspan :  this.cm.getColumnCount()
5840                         }
5841                     ]
5842                 }
5843             ]
5844         };
5845         
5846         return body;
5847     },
5848     
5849     renderFooter : function()
5850     {
5851         var footer = {
5852             tag: 'tfoot',
5853             cn : [
5854                 {
5855                     tag: 'tr',
5856                     cn : [
5857                         {
5858                             tag : 'td',
5859                             colspan :  this.cm.getColumnCount()
5860                         }
5861                     ]
5862                 }
5863             ]
5864         };
5865         
5866         return footer;
5867     },
5868     
5869     
5870     
5871     onLoad : function()
5872     {
5873         Roo.log('ds onload');
5874         this.clear();
5875         
5876         var _this = this;
5877         var cm = this.cm;
5878         var ds = this.store;
5879         
5880         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5881             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5882             
5883             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5884                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5885             }
5886             
5887             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5888                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5889             }
5890         });
5891         
5892         var tbody =  this.mainBody;
5893               
5894         if(ds.getCount() > 0){
5895             ds.data.each(function(d,rowIndex){
5896                 var row =  this.renderRow(cm, ds, rowIndex);
5897                 
5898                 tbody.createChild(row);
5899                 
5900                 var _this = this;
5901                 
5902                 if(row.cellObjects.length){
5903                     Roo.each(row.cellObjects, function(r){
5904                         _this.renderCellObject(r);
5905                     })
5906                 }
5907                 
5908             }, this);
5909         }
5910         
5911         Roo.each(this.el.select('tbody td', true).elements, function(e){
5912             e.on('mouseover', _this.onMouseover, _this);
5913         });
5914         
5915         Roo.each(this.el.select('tbody td', true).elements, function(e){
5916             e.on('mouseout', _this.onMouseout, _this);
5917         });
5918         this.fireEvent('rowsrendered', this);
5919         //if(this.loadMask){
5920         //    this.maskEl.hide();
5921         //}
5922     },
5923     
5924     
5925     onUpdate : function(ds,record)
5926     {
5927         this.refreshRow(record);
5928     },
5929     
5930     onRemove : function(ds, record, index, isUpdate){
5931         if(isUpdate !== true){
5932             this.fireEvent("beforerowremoved", this, index, record);
5933         }
5934         var bt = this.mainBody.dom;
5935         
5936         var rows = this.el.select('tbody > tr', true).elements;
5937         
5938         if(typeof(rows[index]) != 'undefined'){
5939             bt.removeChild(rows[index].dom);
5940         }
5941         
5942 //        if(bt.rows[index]){
5943 //            bt.removeChild(bt.rows[index]);
5944 //        }
5945         
5946         if(isUpdate !== true){
5947             //this.stripeRows(index);
5948             //this.syncRowHeights(index, index);
5949             //this.layout();
5950             this.fireEvent("rowremoved", this, index, record);
5951         }
5952     },
5953     
5954     onAdd : function(ds, records, rowIndex)
5955     {
5956         //Roo.log('on Add called');
5957         // - note this does not handle multiple adding very well..
5958         var bt = this.mainBody.dom;
5959         for (var i =0 ; i < records.length;i++) {
5960             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5961             //Roo.log(records[i]);
5962             //Roo.log(this.store.getAt(rowIndex+i));
5963             this.insertRow(this.store, rowIndex + i, false);
5964             return;
5965         }
5966         
5967     },
5968     
5969     
5970     refreshRow : function(record){
5971         var ds = this.store, index;
5972         if(typeof record == 'number'){
5973             index = record;
5974             record = ds.getAt(index);
5975         }else{
5976             index = ds.indexOf(record);
5977         }
5978         this.insertRow(ds, index, true);
5979         this.onRemove(ds, record, index+1, true);
5980         //this.syncRowHeights(index, index);
5981         //this.layout();
5982         this.fireEvent("rowupdated", this, index, record);
5983     },
5984     
5985     insertRow : function(dm, rowIndex, isUpdate){
5986         
5987         if(!isUpdate){
5988             this.fireEvent("beforerowsinserted", this, rowIndex);
5989         }
5990             //var s = this.getScrollState();
5991         var row = this.renderRow(this.cm, this.store, rowIndex);
5992         // insert before rowIndex..
5993         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5994         
5995         var _this = this;
5996                 
5997         if(row.cellObjects.length){
5998             Roo.each(row.cellObjects, function(r){
5999                 _this.renderCellObject(r);
6000             })
6001         }
6002             
6003         if(!isUpdate){
6004             this.fireEvent("rowsinserted", this, rowIndex);
6005             //this.syncRowHeights(firstRow, lastRow);
6006             //this.stripeRows(firstRow);
6007             //this.layout();
6008         }
6009         
6010     },
6011     
6012     
6013     getRowDom : function(rowIndex)
6014     {
6015         var rows = this.el.select('tbody > tr', true).elements;
6016         
6017         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6018         
6019     },
6020     // returns the object tree for a tr..
6021   
6022     
6023     renderRow : function(cm, ds, rowIndex) 
6024     {
6025         
6026         var d = ds.getAt(rowIndex);
6027         
6028         var row = {
6029             tag : 'tr',
6030             cn : []
6031         };
6032             
6033         var cellObjects = [];
6034         
6035         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6036             var config = cm.config[i];
6037             
6038             var renderer = cm.getRenderer(i);
6039             var value = '';
6040             var id = false;
6041             
6042             if(typeof(renderer) !== 'undefined'){
6043                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6044             }
6045             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6046             // and are rendered into the cells after the row is rendered - using the id for the element.
6047             
6048             if(typeof(value) === 'object'){
6049                 id = Roo.id();
6050                 cellObjects.push({
6051                     container : id,
6052                     cfg : value 
6053                 })
6054             }
6055             
6056             var rowcfg = {
6057                 record: d,
6058                 rowIndex : rowIndex,
6059                 colIndex : i,
6060                 rowClass : ''
6061             }
6062
6063             this.fireEvent('rowclass', this, rowcfg);
6064             
6065             var td = {
6066                 tag: 'td',
6067                 cls : rowcfg.rowClass,
6068                 style: '',
6069                 html: (typeof(value) === 'object') ? '' : value
6070             };
6071             
6072             if (id) {
6073                 td.id = id;
6074             }
6075             
6076             if(typeof(config.colspan) != 'undefined'){
6077                 td.colspan = config.colspan;
6078             }
6079             
6080             if(typeof(config.hidden) != 'undefined' && config.hidden){
6081                 td.style += ' display:none;';
6082             }
6083             
6084             if(typeof(config.align) != 'undefined' && config.align.length){
6085                 td.style += ' text-align:' + config.align + ';';
6086             }
6087             
6088             if(typeof(config.width) != 'undefined'){
6089                 td.style += ' width:' +  config.width + 'px;';
6090             }
6091             
6092             if(typeof(config.cursor) != 'undefined'){
6093                 td.style += ' cursor:' +  config.cursor + ';';
6094             }
6095             
6096             if(typeof(config.cls) != 'undefined'){
6097                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6098             }
6099              
6100             row.cn.push(td);
6101            
6102         }
6103         
6104         row.cellObjects = cellObjects;
6105         
6106         return row;
6107           
6108     },
6109     
6110     
6111     
6112     onBeforeLoad : function()
6113     {
6114         //Roo.log('ds onBeforeLoad');
6115         
6116         //this.clear();
6117         
6118         //if(this.loadMask){
6119         //    this.maskEl.show();
6120         //}
6121     },
6122      /**
6123      * Remove all rows
6124      */
6125     clear : function()
6126     {
6127         this.el.select('tbody', true).first().dom.innerHTML = '';
6128     },
6129     /**
6130      * Show or hide a row.
6131      * @param {Number} rowIndex to show or hide
6132      * @param {Boolean} state hide
6133      */
6134     setRowVisibility : function(rowIndex, state)
6135     {
6136         var bt = this.mainBody.dom;
6137         
6138         var rows = this.el.select('tbody > tr', true).elements;
6139         
6140         if(typeof(rows[rowIndex]) == 'undefined'){
6141             return;
6142         }
6143         rows[rowIndex].dom.style.display = state ? '' : 'none';
6144     },
6145     
6146     
6147     getSelectionModel : function(){
6148         if(!this.selModel){
6149             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6150         }
6151         return this.selModel;
6152     },
6153     /*
6154      * Render the Roo.bootstrap object from renderder
6155      */
6156     renderCellObject : function(r)
6157     {
6158         var _this = this;
6159         
6160         var t = r.cfg.render(r.container);
6161         
6162         if(r.cfg.cn){
6163             Roo.each(r.cfg.cn, function(c){
6164                 var child = {
6165                     container: t.getChildContainer(),
6166                     cfg: c
6167                 }
6168                 _this.renderCellObject(child);
6169             })
6170         }
6171     },
6172     
6173     getRowIndex : function(row)
6174     {
6175         var rowIndex = -1;
6176         
6177         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6178             if(el != row){
6179                 return;
6180             }
6181             
6182             rowIndex = index;
6183         });
6184         
6185         return rowIndex;
6186     }
6187    
6188 });
6189
6190  
6191
6192  /*
6193  * - LGPL
6194  *
6195  * table cell
6196  * 
6197  */
6198
6199 /**
6200  * @class Roo.bootstrap.TableCell
6201  * @extends Roo.bootstrap.Component
6202  * Bootstrap TableCell class
6203  * @cfg {String} html cell contain text
6204  * @cfg {String} cls cell class
6205  * @cfg {String} tag cell tag (td|th) default td
6206  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6207  * @cfg {String} align Aligns the content in a cell
6208  * @cfg {String} axis Categorizes cells
6209  * @cfg {String} bgcolor Specifies the background color of a cell
6210  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6211  * @cfg {Number} colspan Specifies the number of columns a cell should span
6212  * @cfg {String} headers Specifies one or more header cells a cell is related to
6213  * @cfg {Number} height Sets the height of a cell
6214  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6215  * @cfg {Number} rowspan Sets the number of rows a cell should span
6216  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6217  * @cfg {String} valign Vertical aligns the content in a cell
6218  * @cfg {Number} width Specifies the width of a cell
6219  * 
6220  * @constructor
6221  * Create a new TableCell
6222  * @param {Object} config The config object
6223  */
6224
6225 Roo.bootstrap.TableCell = function(config){
6226     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6227 };
6228
6229 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6230     
6231     html: false,
6232     cls: false,
6233     tag: false,
6234     abbr: false,
6235     align: false,
6236     axis: false,
6237     bgcolor: false,
6238     charoff: false,
6239     colspan: false,
6240     headers: false,
6241     height: false,
6242     nowrap: false,
6243     rowspan: false,
6244     scope: false,
6245     valign: false,
6246     width: false,
6247     
6248     
6249     getAutoCreate : function(){
6250         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6251         
6252         cfg = {
6253             tag: 'td'
6254         }
6255         
6256         if(this.tag){
6257             cfg.tag = this.tag;
6258         }
6259         
6260         if (this.html) {
6261             cfg.html=this.html
6262         }
6263         if (this.cls) {
6264             cfg.cls=this.cls
6265         }
6266         if (this.abbr) {
6267             cfg.abbr=this.abbr
6268         }
6269         if (this.align) {
6270             cfg.align=this.align
6271         }
6272         if (this.axis) {
6273             cfg.axis=this.axis
6274         }
6275         if (this.bgcolor) {
6276             cfg.bgcolor=this.bgcolor
6277         }
6278         if (this.charoff) {
6279             cfg.charoff=this.charoff
6280         }
6281         if (this.colspan) {
6282             cfg.colspan=this.colspan
6283         }
6284         if (this.headers) {
6285             cfg.headers=this.headers
6286         }
6287         if (this.height) {
6288             cfg.height=this.height
6289         }
6290         if (this.nowrap) {
6291             cfg.nowrap=this.nowrap
6292         }
6293         if (this.rowspan) {
6294             cfg.rowspan=this.rowspan
6295         }
6296         if (this.scope) {
6297             cfg.scope=this.scope
6298         }
6299         if (this.valign) {
6300             cfg.valign=this.valign
6301         }
6302         if (this.width) {
6303             cfg.width=this.width
6304         }
6305         
6306         
6307         return cfg;
6308     }
6309    
6310 });
6311
6312  
6313
6314  /*
6315  * - LGPL
6316  *
6317  * table row
6318  * 
6319  */
6320
6321 /**
6322  * @class Roo.bootstrap.TableRow
6323  * @extends Roo.bootstrap.Component
6324  * Bootstrap TableRow class
6325  * @cfg {String} cls row class
6326  * @cfg {String} align Aligns the content in a table row
6327  * @cfg {String} bgcolor Specifies a background color for a table row
6328  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6329  * @cfg {String} valign Vertical aligns the content in a table row
6330  * 
6331  * @constructor
6332  * Create a new TableRow
6333  * @param {Object} config The config object
6334  */
6335
6336 Roo.bootstrap.TableRow = function(config){
6337     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6338 };
6339
6340 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6341     
6342     cls: false,
6343     align: false,
6344     bgcolor: false,
6345     charoff: false,
6346     valign: false,
6347     
6348     getAutoCreate : function(){
6349         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6350         
6351         cfg = {
6352             tag: 'tr'
6353         }
6354             
6355         if(this.cls){
6356             cfg.cls = this.cls;
6357         }
6358         if(this.align){
6359             cfg.align = this.align;
6360         }
6361         if(this.bgcolor){
6362             cfg.bgcolor = this.bgcolor;
6363         }
6364         if(this.charoff){
6365             cfg.charoff = this.charoff;
6366         }
6367         if(this.valign){
6368             cfg.valign = this.valign;
6369         }
6370         
6371         return cfg;
6372     }
6373    
6374 });
6375
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * table body
6382  * 
6383  */
6384
6385 /**
6386  * @class Roo.bootstrap.TableBody
6387  * @extends Roo.bootstrap.Component
6388  * Bootstrap TableBody class
6389  * @cfg {String} cls element class
6390  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6391  * @cfg {String} align Aligns the content inside the element
6392  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6393  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6394  * 
6395  * @constructor
6396  * Create a new TableBody
6397  * @param {Object} config The config object
6398  */
6399
6400 Roo.bootstrap.TableBody = function(config){
6401     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6402 };
6403
6404 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6405     
6406     cls: false,
6407     tag: false,
6408     align: false,
6409     charoff: false,
6410     valign: false,
6411     
6412     getAutoCreate : function(){
6413         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6414         
6415         cfg = {
6416             tag: 'tbody'
6417         }
6418             
6419         if (this.cls) {
6420             cfg.cls=this.cls
6421         }
6422         if(this.tag){
6423             cfg.tag = this.tag;
6424         }
6425         
6426         if(this.align){
6427             cfg.align = this.align;
6428         }
6429         if(this.charoff){
6430             cfg.charoff = this.charoff;
6431         }
6432         if(this.valign){
6433             cfg.valign = this.valign;
6434         }
6435         
6436         return cfg;
6437     }
6438     
6439     
6440 //    initEvents : function()
6441 //    {
6442 //        
6443 //        if(!this.store){
6444 //            return;
6445 //        }
6446 //        
6447 //        this.store = Roo.factory(this.store, Roo.data);
6448 //        this.store.on('load', this.onLoad, this);
6449 //        
6450 //        this.store.load();
6451 //        
6452 //    },
6453 //    
6454 //    onLoad: function () 
6455 //    {   
6456 //        this.fireEvent('load', this);
6457 //    }
6458 //    
6459 //   
6460 });
6461
6462  
6463
6464  /*
6465  * Based on:
6466  * Ext JS Library 1.1.1
6467  * Copyright(c) 2006-2007, Ext JS, LLC.
6468  *
6469  * Originally Released Under LGPL - original licence link has changed is not relivant.
6470  *
6471  * Fork - LGPL
6472  * <script type="text/javascript">
6473  */
6474
6475 // as we use this in bootstrap.
6476 Roo.namespace('Roo.form');
6477  /**
6478  * @class Roo.form.Action
6479  * Internal Class used to handle form actions
6480  * @constructor
6481  * @param {Roo.form.BasicForm} el The form element or its id
6482  * @param {Object} config Configuration options
6483  */
6484
6485  
6486  
6487 // define the action interface
6488 Roo.form.Action = function(form, options){
6489     this.form = form;
6490     this.options = options || {};
6491 };
6492 /**
6493  * Client Validation Failed
6494  * @const 
6495  */
6496 Roo.form.Action.CLIENT_INVALID = 'client';
6497 /**
6498  * Server Validation Failed
6499  * @const 
6500  */
6501 Roo.form.Action.SERVER_INVALID = 'server';
6502  /**
6503  * Connect to Server Failed
6504  * @const 
6505  */
6506 Roo.form.Action.CONNECT_FAILURE = 'connect';
6507 /**
6508  * Reading Data from Server Failed
6509  * @const 
6510  */
6511 Roo.form.Action.LOAD_FAILURE = 'load';
6512
6513 Roo.form.Action.prototype = {
6514     type : 'default',
6515     failureType : undefined,
6516     response : undefined,
6517     result : undefined,
6518
6519     // interface method
6520     run : function(options){
6521
6522     },
6523
6524     // interface method
6525     success : function(response){
6526
6527     },
6528
6529     // interface method
6530     handleResponse : function(response){
6531
6532     },
6533
6534     // default connection failure
6535     failure : function(response){
6536         
6537         this.response = response;
6538         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6539         this.form.afterAction(this, false);
6540     },
6541
6542     processResponse : function(response){
6543         this.response = response;
6544         if(!response.responseText){
6545             return true;
6546         }
6547         this.result = this.handleResponse(response);
6548         return this.result;
6549     },
6550
6551     // utility functions used internally
6552     getUrl : function(appendParams){
6553         var url = this.options.url || this.form.url || this.form.el.dom.action;
6554         if(appendParams){
6555             var p = this.getParams();
6556             if(p){
6557                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6558             }
6559         }
6560         return url;
6561     },
6562
6563     getMethod : function(){
6564         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6565     },
6566
6567     getParams : function(){
6568         var bp = this.form.baseParams;
6569         var p = this.options.params;
6570         if(p){
6571             if(typeof p == "object"){
6572                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6573             }else if(typeof p == 'string' && bp){
6574                 p += '&' + Roo.urlEncode(bp);
6575             }
6576         }else if(bp){
6577             p = Roo.urlEncode(bp);
6578         }
6579         return p;
6580     },
6581
6582     createCallback : function(){
6583         return {
6584             success: this.success,
6585             failure: this.failure,
6586             scope: this,
6587             timeout: (this.form.timeout*1000),
6588             upload: this.form.fileUpload ? this.success : undefined
6589         };
6590     }
6591 };
6592
6593 Roo.form.Action.Submit = function(form, options){
6594     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6595 };
6596
6597 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6598     type : 'submit',
6599
6600     haveProgress : false,
6601     uploadComplete : false,
6602     
6603     // uploadProgress indicator.
6604     uploadProgress : function()
6605     {
6606         if (!this.form.progressUrl) {
6607             return;
6608         }
6609         
6610         if (!this.haveProgress) {
6611             Roo.MessageBox.progress("Uploading", "Uploading");
6612         }
6613         if (this.uploadComplete) {
6614            Roo.MessageBox.hide();
6615            return;
6616         }
6617         
6618         this.haveProgress = true;
6619    
6620         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6621         
6622         var c = new Roo.data.Connection();
6623         c.request({
6624             url : this.form.progressUrl,
6625             params: {
6626                 id : uid
6627             },
6628             method: 'GET',
6629             success : function(req){
6630                //console.log(data);
6631                 var rdata = false;
6632                 var edata;
6633                 try  {
6634                    rdata = Roo.decode(req.responseText)
6635                 } catch (e) {
6636                     Roo.log("Invalid data from server..");
6637                     Roo.log(edata);
6638                     return;
6639                 }
6640                 if (!rdata || !rdata.success) {
6641                     Roo.log(rdata);
6642                     Roo.MessageBox.alert(Roo.encode(rdata));
6643                     return;
6644                 }
6645                 var data = rdata.data;
6646                 
6647                 if (this.uploadComplete) {
6648                    Roo.MessageBox.hide();
6649                    return;
6650                 }
6651                    
6652                 if (data){
6653                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6654                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6655                     );
6656                 }
6657                 this.uploadProgress.defer(2000,this);
6658             },
6659        
6660             failure: function(data) {
6661                 Roo.log('progress url failed ');
6662                 Roo.log(data);
6663             },
6664             scope : this
6665         });
6666            
6667     },
6668     
6669     
6670     run : function()
6671     {
6672         // run get Values on the form, so it syncs any secondary forms.
6673         this.form.getValues();
6674         
6675         var o = this.options;
6676         var method = this.getMethod();
6677         var isPost = method == 'POST';
6678         if(o.clientValidation === false || this.form.isValid()){
6679             
6680             if (this.form.progressUrl) {
6681                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6682                     (new Date() * 1) + '' + Math.random());
6683                     
6684             } 
6685             
6686             
6687             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6688                 form:this.form.el.dom,
6689                 url:this.getUrl(!isPost),
6690                 method: method,
6691                 params:isPost ? this.getParams() : null,
6692                 isUpload: this.form.fileUpload
6693             }));
6694             
6695             this.uploadProgress();
6696
6697         }else if (o.clientValidation !== false){ // client validation failed
6698             this.failureType = Roo.form.Action.CLIENT_INVALID;
6699             this.form.afterAction(this, false);
6700         }
6701     },
6702
6703     success : function(response)
6704     {
6705         this.uploadComplete= true;
6706         if (this.haveProgress) {
6707             Roo.MessageBox.hide();
6708         }
6709         
6710         
6711         var result = this.processResponse(response);
6712         if(result === true || result.success){
6713             this.form.afterAction(this, true);
6714             return;
6715         }
6716         if(result.errors){
6717             this.form.markInvalid(result.errors);
6718             this.failureType = Roo.form.Action.SERVER_INVALID;
6719         }
6720         this.form.afterAction(this, false);
6721     },
6722     failure : function(response)
6723     {
6724         this.uploadComplete= true;
6725         if (this.haveProgress) {
6726             Roo.MessageBox.hide();
6727         }
6728         
6729         this.response = response;
6730         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6731         this.form.afterAction(this, false);
6732     },
6733     
6734     handleResponse : function(response){
6735         if(this.form.errorReader){
6736             var rs = this.form.errorReader.read(response);
6737             var errors = [];
6738             if(rs.records){
6739                 for(var i = 0, len = rs.records.length; i < len; i++) {
6740                     var r = rs.records[i];
6741                     errors[i] = r.data;
6742                 }
6743             }
6744             if(errors.length < 1){
6745                 errors = null;
6746             }
6747             return {
6748                 success : rs.success,
6749                 errors : errors
6750             };
6751         }
6752         var ret = false;
6753         try {
6754             ret = Roo.decode(response.responseText);
6755         } catch (e) {
6756             ret = {
6757                 success: false,
6758                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6759                 errors : []
6760             };
6761         }
6762         return ret;
6763         
6764     }
6765 });
6766
6767
6768 Roo.form.Action.Load = function(form, options){
6769     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6770     this.reader = this.form.reader;
6771 };
6772
6773 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6774     type : 'load',
6775
6776     run : function(){
6777         
6778         Roo.Ajax.request(Roo.apply(
6779                 this.createCallback(), {
6780                     method:this.getMethod(),
6781                     url:this.getUrl(false),
6782                     params:this.getParams()
6783         }));
6784     },
6785
6786     success : function(response){
6787         
6788         var result = this.processResponse(response);
6789         if(result === true || !result.success || !result.data){
6790             this.failureType = Roo.form.Action.LOAD_FAILURE;
6791             this.form.afterAction(this, false);
6792             return;
6793         }
6794         this.form.clearInvalid();
6795         this.form.setValues(result.data);
6796         this.form.afterAction(this, true);
6797     },
6798
6799     handleResponse : function(response){
6800         if(this.form.reader){
6801             var rs = this.form.reader.read(response);
6802             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6803             return {
6804                 success : rs.success,
6805                 data : data
6806             };
6807         }
6808         return Roo.decode(response.responseText);
6809     }
6810 });
6811
6812 Roo.form.Action.ACTION_TYPES = {
6813     'load' : Roo.form.Action.Load,
6814     'submit' : Roo.form.Action.Submit
6815 };/*
6816  * - LGPL
6817  *
6818  * form
6819  * 
6820  */
6821
6822 /**
6823  * @class Roo.bootstrap.Form
6824  * @extends Roo.bootstrap.Component
6825  * Bootstrap Form class
6826  * @cfg {String} method  GET | POST (default POST)
6827  * @cfg {String} labelAlign top | left (default top)
6828  * @cfg {String} align left  | right - for navbars
6829  * @cfg {Boolean} loadMask load mask when submit (default true)
6830
6831  * 
6832  * @constructor
6833  * Create a new Form
6834  * @param {Object} config The config object
6835  */
6836
6837
6838 Roo.bootstrap.Form = function(config){
6839     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6840     this.addEvents({
6841         /**
6842          * @event clientvalidation
6843          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6844          * @param {Form} this
6845          * @param {Boolean} valid true if the form has passed client-side validation
6846          */
6847         clientvalidation: true,
6848         /**
6849          * @event beforeaction
6850          * Fires before any action is performed. Return false to cancel the action.
6851          * @param {Form} this
6852          * @param {Action} action The action to be performed
6853          */
6854         beforeaction: true,
6855         /**
6856          * @event actionfailed
6857          * Fires when an action fails.
6858          * @param {Form} this
6859          * @param {Action} action The action that failed
6860          */
6861         actionfailed : true,
6862         /**
6863          * @event actioncomplete
6864          * Fires when an action is completed.
6865          * @param {Form} this
6866          * @param {Action} action The action that completed
6867          */
6868         actioncomplete : true
6869     });
6870     
6871 };
6872
6873 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6874       
6875      /**
6876      * @cfg {String} method
6877      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6878      */
6879     method : 'POST',
6880     /**
6881      * @cfg {String} url
6882      * The URL to use for form actions if one isn't supplied in the action options.
6883      */
6884     /**
6885      * @cfg {Boolean} fileUpload
6886      * Set to true if this form is a file upload.
6887      */
6888      
6889     /**
6890      * @cfg {Object} baseParams
6891      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6892      */
6893       
6894     /**
6895      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6896      */
6897     timeout: 30,
6898     /**
6899      * @cfg {Sting} align (left|right) for navbar forms
6900      */
6901     align : 'left',
6902
6903     // private
6904     activeAction : null,
6905  
6906     /**
6907      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6908      * element by passing it or its id or mask the form itself by passing in true.
6909      * @type Mixed
6910      */
6911     waitMsgTarget : false,
6912     
6913     loadMask : true,
6914     
6915     getAutoCreate : function(){
6916         
6917         var cfg = {
6918             tag: 'form',
6919             method : this.method || 'POST',
6920             id : this.id || Roo.id(),
6921             cls : ''
6922         }
6923         if (this.parent().xtype.match(/^Nav/)) {
6924             cfg.cls = 'navbar-form navbar-' + this.align;
6925             
6926         }
6927         
6928         if (this.labelAlign == 'left' ) {
6929             cfg.cls += ' form-horizontal';
6930         }
6931         
6932         
6933         return cfg;
6934     },
6935     initEvents : function()
6936     {
6937         this.el.on('submit', this.onSubmit, this);
6938         // this was added as random key presses on the form where triggering form submit.
6939         this.el.on('keypress', function(e) {
6940             if (e.getCharCode() != 13) {
6941                 return true;
6942             }
6943             // we might need to allow it for textareas.. and some other items.
6944             // check e.getTarget().
6945             
6946             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6947                 return true;
6948             }
6949         
6950             Roo.log("keypress blocked");
6951             
6952             e.preventDefault();
6953             return false;
6954         });
6955         
6956     },
6957     // private
6958     onSubmit : function(e){
6959         e.stopEvent();
6960     },
6961     
6962      /**
6963      * Returns true if client-side validation on the form is successful.
6964      * @return Boolean
6965      */
6966     isValid : function(){
6967         var items = this.getItems();
6968         var valid = true;
6969         items.each(function(f){
6970            if(!f.validate()){
6971                valid = false;
6972                
6973            }
6974         });
6975         return valid;
6976     },
6977     /**
6978      * Returns true if any fields in this form have changed since their original load.
6979      * @return Boolean
6980      */
6981     isDirty : function(){
6982         var dirty = false;
6983         var items = this.getItems();
6984         items.each(function(f){
6985            if(f.isDirty()){
6986                dirty = true;
6987                return false;
6988            }
6989            return true;
6990         });
6991         return dirty;
6992     },
6993      /**
6994      * Performs a predefined action (submit or load) or custom actions you define on this form.
6995      * @param {String} actionName The name of the action type
6996      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6997      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6998      * accept other config options):
6999      * <pre>
7000 Property          Type             Description
7001 ----------------  ---------------  ----------------------------------------------------------------------------------
7002 url               String           The url for the action (defaults to the form's url)
7003 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7004 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7005 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7006                                    validate the form on the client (defaults to false)
7007      * </pre>
7008      * @return {BasicForm} this
7009      */
7010     doAction : function(action, options){
7011         if(typeof action == 'string'){
7012             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7013         }
7014         if(this.fireEvent('beforeaction', this, action) !== false){
7015             this.beforeAction(action);
7016             action.run.defer(100, action);
7017         }
7018         return this;
7019     },
7020     
7021     // private
7022     beforeAction : function(action){
7023         var o = action.options;
7024         
7025         if(this.loadMask){
7026             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7027         }
7028         // not really supported yet.. ??
7029         
7030         //if(this.waitMsgTarget === true){
7031         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7032         //}else if(this.waitMsgTarget){
7033         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7034         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7035         //}else {
7036         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7037        // }
7038          
7039     },
7040
7041     // private
7042     afterAction : function(action, success){
7043         this.activeAction = null;
7044         var o = action.options;
7045         
7046         //if(this.waitMsgTarget === true){
7047             this.el.unmask();
7048         //}else if(this.waitMsgTarget){
7049         //    this.waitMsgTarget.unmask();
7050         //}else{
7051         //    Roo.MessageBox.updateProgress(1);
7052         //    Roo.MessageBox.hide();
7053        // }
7054         // 
7055         if(success){
7056             if(o.reset){
7057                 this.reset();
7058             }
7059             Roo.callback(o.success, o.scope, [this, action]);
7060             this.fireEvent('actioncomplete', this, action);
7061             
7062         }else{
7063             
7064             // failure condition..
7065             // we have a scenario where updates need confirming.
7066             // eg. if a locking scenario exists..
7067             // we look for { errors : { needs_confirm : true }} in the response.
7068             if (
7069                 (typeof(action.result) != 'undefined')  &&
7070                 (typeof(action.result.errors) != 'undefined')  &&
7071                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7072            ){
7073                 var _t = this;
7074                 Roo.log("not supported yet");
7075                  /*
7076                 
7077                 Roo.MessageBox.confirm(
7078                     "Change requires confirmation",
7079                     action.result.errorMsg,
7080                     function(r) {
7081                         if (r != 'yes') {
7082                             return;
7083                         }
7084                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7085                     }
7086                     
7087                 );
7088                 */
7089                 
7090                 
7091                 return;
7092             }
7093             
7094             Roo.callback(o.failure, o.scope, [this, action]);
7095             // show an error message if no failed handler is set..
7096             if (!this.hasListener('actionfailed')) {
7097                 Roo.log("need to add dialog support");
7098                 /*
7099                 Roo.MessageBox.alert("Error",
7100                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7101                         action.result.errorMsg :
7102                         "Saving Failed, please check your entries or try again"
7103                 );
7104                 */
7105             }
7106             
7107             this.fireEvent('actionfailed', this, action);
7108         }
7109         
7110     },
7111     /**
7112      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7113      * @param {String} id The value to search for
7114      * @return Field
7115      */
7116     findField : function(id){
7117         var items = this.getItems();
7118         var field = items.get(id);
7119         if(!field){
7120              items.each(function(f){
7121                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7122                     field = f;
7123                     return false;
7124                 }
7125                 return true;
7126             });
7127         }
7128         return field || null;
7129     },
7130      /**
7131      * Mark fields in this form invalid in bulk.
7132      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7133      * @return {BasicForm} this
7134      */
7135     markInvalid : function(errors){
7136         if(errors instanceof Array){
7137             for(var i = 0, len = errors.length; i < len; i++){
7138                 var fieldError = errors[i];
7139                 var f = this.findField(fieldError.id);
7140                 if(f){
7141                     f.markInvalid(fieldError.msg);
7142                 }
7143             }
7144         }else{
7145             var field, id;
7146             for(id in errors){
7147                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7148                     field.markInvalid(errors[id]);
7149                 }
7150             }
7151         }
7152         //Roo.each(this.childForms || [], function (f) {
7153         //    f.markInvalid(errors);
7154         //});
7155         
7156         return this;
7157     },
7158
7159     /**
7160      * Set values for fields in this form in bulk.
7161      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7162      * @return {BasicForm} this
7163      */
7164     setValues : function(values){
7165         if(values instanceof Array){ // array of objects
7166             for(var i = 0, len = values.length; i < len; i++){
7167                 var v = values[i];
7168                 var f = this.findField(v.id);
7169                 if(f){
7170                     f.setValue(v.value);
7171                     if(this.trackResetOnLoad){
7172                         f.originalValue = f.getValue();
7173                     }
7174                 }
7175             }
7176         }else{ // object hash
7177             var field, id;
7178             for(id in values){
7179                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7180                     
7181                     if (field.setFromData && 
7182                         field.valueField && 
7183                         field.displayField &&
7184                         // combos' with local stores can 
7185                         // be queried via setValue()
7186                         // to set their value..
7187                         (field.store && !field.store.isLocal)
7188                         ) {
7189                         // it's a combo
7190                         var sd = { };
7191                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7192                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7193                         field.setFromData(sd);
7194                         
7195                     } else {
7196                         field.setValue(values[id]);
7197                     }
7198                     
7199                     
7200                     if(this.trackResetOnLoad){
7201                         field.originalValue = field.getValue();
7202                     }
7203                 }
7204             }
7205         }
7206          
7207         //Roo.each(this.childForms || [], function (f) {
7208         //    f.setValues(values);
7209         //});
7210                 
7211         return this;
7212     },
7213
7214     /**
7215      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7216      * they are returned as an array.
7217      * @param {Boolean} asString
7218      * @return {Object}
7219      */
7220     getValues : function(asString){
7221         //if (this.childForms) {
7222             // copy values from the child forms
7223         //    Roo.each(this.childForms, function (f) {
7224         //        this.setValues(f.getValues());
7225         //    }, this);
7226         //}
7227         
7228         
7229         
7230         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7231         if(asString === true){
7232             return fs;
7233         }
7234         return Roo.urlDecode(fs);
7235     },
7236     
7237     /**
7238      * Returns the fields in this form as an object with key/value pairs. 
7239      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7240      * @return {Object}
7241      */
7242     getFieldValues : function(with_hidden)
7243     {
7244         var items = this.getItems();
7245         var ret = {};
7246         items.each(function(f){
7247             if (!f.getName()) {
7248                 return;
7249             }
7250             var v = f.getValue();
7251             if (f.inputType =='radio') {
7252                 if (typeof(ret[f.getName()]) == 'undefined') {
7253                     ret[f.getName()] = ''; // empty..
7254                 }
7255                 
7256                 if (!f.el.dom.checked) {
7257                     return;
7258                     
7259                 }
7260                 v = f.el.dom.value;
7261                 
7262             }
7263             
7264             // not sure if this supported any more..
7265             if ((typeof(v) == 'object') && f.getRawValue) {
7266                 v = f.getRawValue() ; // dates..
7267             }
7268             // combo boxes where name != hiddenName...
7269             if (f.name != f.getName()) {
7270                 ret[f.name] = f.getRawValue();
7271             }
7272             ret[f.getName()] = v;
7273         });
7274         
7275         return ret;
7276     },
7277
7278     /**
7279      * Clears all invalid messages in this form.
7280      * @return {BasicForm} this
7281      */
7282     clearInvalid : function(){
7283         var items = this.getItems();
7284         
7285         items.each(function(f){
7286            f.clearInvalid();
7287         });
7288         
7289         
7290         
7291         return this;
7292     },
7293
7294     /**
7295      * Resets this form.
7296      * @return {BasicForm} this
7297      */
7298     reset : function(){
7299         var items = this.getItems();
7300         items.each(function(f){
7301             f.reset();
7302         });
7303         
7304         Roo.each(this.childForms || [], function (f) {
7305             f.reset();
7306         });
7307        
7308         
7309         return this;
7310     },
7311     getItems : function()
7312     {
7313         var r=new Roo.util.MixedCollection(false, function(o){
7314             return o.id || (o.id = Roo.id());
7315         });
7316         var iter = function(el) {
7317             if (el.inputEl) {
7318                 r.add(el);
7319             }
7320             if (!el.items) {
7321                 return;
7322             }
7323             Roo.each(el.items,function(e) {
7324                 iter(e);
7325             });
7326             
7327             
7328         };
7329         
7330         iter(this);
7331         return r;
7332         
7333         
7334         
7335         
7336     }
7337     
7338 });
7339
7340  
7341 /*
7342  * Based on:
7343  * Ext JS Library 1.1.1
7344  * Copyright(c) 2006-2007, Ext JS, LLC.
7345  *
7346  * Originally Released Under LGPL - original licence link has changed is not relivant.
7347  *
7348  * Fork - LGPL
7349  * <script type="text/javascript">
7350  */
7351 /**
7352  * @class Roo.form.VTypes
7353  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7354  * @singleton
7355  */
7356 Roo.form.VTypes = function(){
7357     // closure these in so they are only created once.
7358     var alpha = /^[a-zA-Z_]+$/;
7359     var alphanum = /^[a-zA-Z0-9_]+$/;
7360     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7361     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7362
7363     // All these messages and functions are configurable
7364     return {
7365         /**
7366          * The function used to validate email addresses
7367          * @param {String} value The email address
7368          */
7369         'email' : function(v){
7370             return email.test(v);
7371         },
7372         /**
7373          * The error text to display when the email validation function returns false
7374          * @type String
7375          */
7376         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7377         /**
7378          * The keystroke filter mask to be applied on email input
7379          * @type RegExp
7380          */
7381         'emailMask' : /[a-z0-9_\.\-@]/i,
7382
7383         /**
7384          * The function used to validate URLs
7385          * @param {String} value The URL
7386          */
7387         'url' : function(v){
7388             return url.test(v);
7389         },
7390         /**
7391          * The error text to display when the url validation function returns false
7392          * @type String
7393          */
7394         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7395         
7396         /**
7397          * The function used to validate alpha values
7398          * @param {String} value The value
7399          */
7400         'alpha' : function(v){
7401             return alpha.test(v);
7402         },
7403         /**
7404          * The error text to display when the alpha validation function returns false
7405          * @type String
7406          */
7407         'alphaText' : 'This field should only contain letters and _',
7408         /**
7409          * The keystroke filter mask to be applied on alpha input
7410          * @type RegExp
7411          */
7412         'alphaMask' : /[a-z_]/i,
7413
7414         /**
7415          * The function used to validate alphanumeric values
7416          * @param {String} value The value
7417          */
7418         'alphanum' : function(v){
7419             return alphanum.test(v);
7420         },
7421         /**
7422          * The error text to display when the alphanumeric validation function returns false
7423          * @type String
7424          */
7425         'alphanumText' : 'This field should only contain letters, numbers and _',
7426         /**
7427          * The keystroke filter mask to be applied on alphanumeric input
7428          * @type RegExp
7429          */
7430         'alphanumMask' : /[a-z0-9_]/i
7431     };
7432 }();/*
7433  * - LGPL
7434  *
7435  * Input
7436  * 
7437  */
7438
7439 /**
7440  * @class Roo.bootstrap.Input
7441  * @extends Roo.bootstrap.Component
7442  * Bootstrap Input class
7443  * @cfg {Boolean} disabled is it disabled
7444  * @cfg {String} fieldLabel - the label associated
7445  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7446  * @cfg {String} name name of the input
7447  * @cfg {string} fieldLabel - the label associated
7448  * @cfg {string}  inputType - input / file submit ...
7449  * @cfg {string} placeholder - placeholder to put in text.
7450  * @cfg {string}  before - input group add on before
7451  * @cfg {string} after - input group add on after
7452  * @cfg {string} size - (lg|sm) or leave empty..
7453  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7454  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7455  * @cfg {Number} md colspan out of 12 for computer-sized screens
7456  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7457  * @cfg {string} value default value of the input
7458  * @cfg {Number} labelWidth set the width of label (0-12)
7459  * @cfg {String} labelAlign (top|left)
7460  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7461  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7462
7463  * @cfg {String} align (left|center|right) Default left
7464  * 
7465  * 
7466  * 
7467  * @constructor
7468  * Create a new Input
7469  * @param {Object} config The config object
7470  */
7471
7472 Roo.bootstrap.Input = function(config){
7473     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7474    
7475         this.addEvents({
7476             /**
7477              * @event focus
7478              * Fires when this field receives input focus.
7479              * @param {Roo.form.Field} this
7480              */
7481             focus : true,
7482             /**
7483              * @event blur
7484              * Fires when this field loses input focus.
7485              * @param {Roo.form.Field} this
7486              */
7487             blur : true,
7488             /**
7489              * @event specialkey
7490              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7491              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7492              * @param {Roo.form.Field} this
7493              * @param {Roo.EventObject} e The event object
7494              */
7495             specialkey : true,
7496             /**
7497              * @event change
7498              * Fires just before the field blurs if the field value has changed.
7499              * @param {Roo.form.Field} this
7500              * @param {Mixed} newValue The new value
7501              * @param {Mixed} oldValue The original value
7502              */
7503             change : true,
7504             /**
7505              * @event invalid
7506              * Fires after the field has been marked as invalid.
7507              * @param {Roo.form.Field} this
7508              * @param {String} msg The validation message
7509              */
7510             invalid : true,
7511             /**
7512              * @event valid
7513              * Fires after the field has been validated with no errors.
7514              * @param {Roo.form.Field} this
7515              */
7516             valid : true,
7517              /**
7518              * @event keyup
7519              * Fires after the key up
7520              * @param {Roo.form.Field} this
7521              * @param {Roo.EventObject}  e The event Object
7522              */
7523             keyup : true
7524         });
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7528      /**
7529      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7530       automatic validation (defaults to "keyup").
7531      */
7532     validationEvent : "keyup",
7533      /**
7534      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7535      */
7536     validateOnBlur : true,
7537     /**
7538      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7539      */
7540     validationDelay : 250,
7541      /**
7542      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7543      */
7544     focusClass : "x-form-focus",  // not needed???
7545     
7546        
7547     /**
7548      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7549      */
7550     invalidClass : "has-warning",
7551     
7552     /**
7553      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7554      */
7555     validClass : "has-success",
7556     
7557     /**
7558      * @cfg {Boolean} hasFeedback (true|false) default true
7559      */
7560     hasFeedback : true,
7561     
7562     /**
7563      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7564      */
7565     invalidFeedbackClass : "glyphicon-warning-sign",
7566     
7567     /**
7568      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7569      */
7570     validFeedbackClass : "glyphicon-ok",
7571     
7572     /**
7573      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7574      */
7575     selectOnFocus : false,
7576     
7577      /**
7578      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7579      */
7580     maskRe : null,
7581        /**
7582      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7583      */
7584     vtype : null,
7585     
7586       /**
7587      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7588      */
7589     disableKeyFilter : false,
7590     
7591        /**
7592      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7593      */
7594     disabled : false,
7595      /**
7596      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7597      */
7598     allowBlank : true,
7599     /**
7600      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7601      */
7602     blankText : "This field is required",
7603     
7604      /**
7605      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7606      */
7607     minLength : 0,
7608     /**
7609      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7610      */
7611     maxLength : Number.MAX_VALUE,
7612     /**
7613      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7614      */
7615     minLengthText : "The minimum length for this field is {0}",
7616     /**
7617      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7618      */
7619     maxLengthText : "The maximum length for this field is {0}",
7620   
7621     
7622     /**
7623      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7624      * If available, this function will be called only after the basic validators all return true, and will be passed the
7625      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7626      */
7627     validator : null,
7628     /**
7629      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7630      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7631      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7632      */
7633     regex : null,
7634     /**
7635      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7636      */
7637     regexText : "",
7638     
7639     autocomplete: false,
7640     
7641     
7642     fieldLabel : '',
7643     inputType : 'text',
7644     
7645     name : false,
7646     placeholder: false,
7647     before : false,
7648     after : false,
7649     size : false,
7650     hasFocus : false,
7651     preventMark: false,
7652     isFormField : true,
7653     value : '',
7654     labelWidth : 2,
7655     labelAlign : false,
7656     readOnly : false,
7657     align : false,
7658     formatedValue : false,
7659     
7660     parentLabelAlign : function()
7661     {
7662         var parent = this;
7663         while (parent.parent()) {
7664             parent = parent.parent();
7665             if (typeof(parent.labelAlign) !='undefined') {
7666                 return parent.labelAlign;
7667             }
7668         }
7669         return 'left';
7670         
7671     },
7672     
7673     getAutoCreate : function(){
7674         
7675         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7676         
7677         var id = Roo.id();
7678         
7679         var cfg = {};
7680         
7681         if(this.inputType != 'hidden'){
7682             cfg.cls = 'form-group' //input-group
7683         }
7684         
7685         var input =  {
7686             tag: 'input',
7687             id : id,
7688             type : this.inputType,
7689             value : this.value,
7690             cls : 'form-control',
7691             placeholder : this.placeholder || '',
7692             autocomplete : this.autocomplete || 'new-password'
7693         };
7694         
7695         
7696         if(this.align){
7697             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7698         }
7699         
7700         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7701             input.maxLength = this.maxLength;
7702         }
7703         
7704         if (this.disabled) {
7705             input.disabled=true;
7706         }
7707         
7708         if (this.readOnly) {
7709             input.readonly=true;
7710         }
7711         
7712         if (this.name) {
7713             input.name = this.name;
7714         }
7715         if (this.size) {
7716             input.cls += ' input-' + this.size;
7717         }
7718         var settings=this;
7719         ['xs','sm','md','lg'].map(function(size){
7720             if (settings[size]) {
7721                 cfg.cls += ' col-' + size + '-' + settings[size];
7722             }
7723         });
7724         
7725         var inputblock = input;
7726         
7727         var feedback = {
7728             tag: 'span',
7729             cls: 'glyphicon form-control-feedback'
7730         };
7731             
7732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7733             
7734             inputblock = {
7735                 cls : 'has-feedback',
7736                 cn :  [
7737                     input,
7738                     feedback
7739                 ] 
7740             };  
7741         }
7742         
7743         if (this.before || this.after) {
7744             
7745             inputblock = {
7746                 cls : 'input-group',
7747                 cn :  [] 
7748             };
7749             
7750             if (this.before && typeof(this.before) == 'string') {
7751                 
7752                 inputblock.cn.push({
7753                     tag :'span',
7754                     cls : 'roo-input-before input-group-addon',
7755                     html : this.before
7756                 });
7757             }
7758             if (this.before && typeof(this.before) == 'object') {
7759                 this.before = Roo.factory(this.before);
7760                 Roo.log(this.before);
7761                 inputblock.cn.push({
7762                     tag :'span',
7763                     cls : 'roo-input-before input-group-' +
7764                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7765                 });
7766             }
7767             
7768             inputblock.cn.push(input);
7769             
7770             if (this.after && typeof(this.after) == 'string') {
7771                 inputblock.cn.push({
7772                     tag :'span',
7773                     cls : 'roo-input-after input-group-addon',
7774                     html : this.after
7775                 });
7776             }
7777             if (this.after && typeof(this.after) == 'object') {
7778                 this.after = Roo.factory(this.after);
7779                 Roo.log(this.after);
7780                 inputblock.cn.push({
7781                     tag :'span',
7782                     cls : 'roo-input-after input-group-' +
7783                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7784                 });
7785             }
7786             
7787             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7788                 inputblock.cls += ' has-feedback';
7789                 inputblock.cn.push(feedback);
7790             }
7791         };
7792         
7793         if (align ==='left' && this.fieldLabel.length) {
7794                 Roo.log("left and has label");
7795                 cfg.cn = [
7796                     
7797                     {
7798                         tag: 'label',
7799                         'for' :  id,
7800                         cls : 'control-label col-sm-' + this.labelWidth,
7801                         html : this.fieldLabel
7802                         
7803                     },
7804                     {
7805                         cls : "col-sm-" + (12 - this.labelWidth), 
7806                         cn: [
7807                             inputblock
7808                         ]
7809                     }
7810                     
7811                 ];
7812         } else if ( this.fieldLabel.length) {
7813                 Roo.log(" label");
7814                  cfg.cn = [
7815                    
7816                     {
7817                         tag: 'label',
7818                         //cls : 'input-group-addon',
7819                         html : this.fieldLabel
7820                         
7821                     },
7822                     
7823                     inputblock
7824                     
7825                 ];
7826
7827         } else {
7828             
7829                 Roo.log(" no label && no align");
7830                 cfg.cn = [
7831                     
7832                         inputblock
7833                     
7834                 ];
7835                 
7836                 
7837         };
7838         Roo.log('input-parentType: ' + this.parentType);
7839         
7840         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7841            cfg.cls += ' navbar-form';
7842            Roo.log(cfg);
7843         }
7844         
7845         return cfg;
7846         
7847     },
7848     /**
7849      * return the real input element.
7850      */
7851     inputEl: function ()
7852     {
7853         return this.el.select('input.form-control',true).first();
7854     },
7855     
7856     tooltipEl : function()
7857     {
7858         return this.inputEl();
7859     },
7860     
7861     setDisabled : function(v)
7862     {
7863         var i  = this.inputEl().dom;
7864         if (!v) {
7865             i.removeAttribute('disabled');
7866             return;
7867             
7868         }
7869         i.setAttribute('disabled','true');
7870     },
7871     initEvents : function()
7872     {
7873           
7874         this.inputEl().on("keydown" , this.fireKey,  this);
7875         this.inputEl().on("focus", this.onFocus,  this);
7876         this.inputEl().on("blur", this.onBlur,  this);
7877         
7878         this.inputEl().relayEvent('keyup', this);
7879
7880         // reference to original value for reset
7881         this.originalValue = this.getValue();
7882         //Roo.form.TextField.superclass.initEvents.call(this);
7883         if(this.validationEvent == 'keyup'){
7884             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7885             this.inputEl().on('keyup', this.filterValidation, this);
7886         }
7887         else if(this.validationEvent !== false){
7888             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7889         }
7890         
7891         if(this.selectOnFocus){
7892             this.on("focus", this.preFocus, this);
7893             
7894         }
7895         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7896             this.inputEl().on("keypress", this.filterKeys, this);
7897         }
7898        /* if(this.grow){
7899             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7900             this.el.on("click", this.autoSize,  this);
7901         }
7902         */
7903         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7904             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7905         }
7906         
7907         if (typeof(this.before) == 'object') {
7908             this.before.render(this.el.select('.roo-input-before',true).first());
7909         }
7910         if (typeof(this.after) == 'object') {
7911             this.after.render(this.el.select('.roo-input-after',true).first());
7912         }
7913         
7914         
7915     },
7916     filterValidation : function(e){
7917         if(!e.isNavKeyPress()){
7918             this.validationTask.delay(this.validationDelay);
7919         }
7920     },
7921      /**
7922      * Validates the field value
7923      * @return {Boolean} True if the value is valid, else false
7924      */
7925     validate : function(){
7926         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7927         if(this.disabled || this.validateValue(this.getRawValue())){
7928             this.markValid();
7929             return true;
7930         }
7931         
7932         this.markInvalid();
7933         return false;
7934     },
7935     
7936     
7937     /**
7938      * Validates a value according to the field's validation rules and marks the field as invalid
7939      * if the validation fails
7940      * @param {Mixed} value The value to validate
7941      * @return {Boolean} True if the value is valid, else false
7942      */
7943     validateValue : function(value){
7944         if(value.length < 1)  { // if it's blank
7945             if(this.allowBlank){
7946                 return true;
7947             }
7948             return false;
7949         }
7950         
7951         if(value.length < this.minLength){
7952             return false;
7953         }
7954         if(value.length > this.maxLength){
7955             return false;
7956         }
7957         if(this.vtype){
7958             var vt = Roo.form.VTypes;
7959             if(!vt[this.vtype](value, this)){
7960                 return false;
7961             }
7962         }
7963         if(typeof this.validator == "function"){
7964             var msg = this.validator(value);
7965             if(msg !== true){
7966                 return false;
7967             }
7968         }
7969         
7970         if(this.regex && !this.regex.test(value)){
7971             return false;
7972         }
7973         
7974         return true;
7975     },
7976
7977     
7978     
7979      // private
7980     fireKey : function(e){
7981         //Roo.log('field ' + e.getKey());
7982         if(e.isNavKeyPress()){
7983             this.fireEvent("specialkey", this, e);
7984         }
7985     },
7986     focus : function (selectText){
7987         if(this.rendered){
7988             this.inputEl().focus();
7989             if(selectText === true){
7990                 this.inputEl().dom.select();
7991             }
7992         }
7993         return this;
7994     } ,
7995     
7996     onFocus : function(){
7997         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7998            // this.el.addClass(this.focusClass);
7999         }
8000         if(!this.hasFocus){
8001             this.hasFocus = true;
8002             this.startValue = this.getValue();
8003             this.fireEvent("focus", this);
8004         }
8005     },
8006     
8007     beforeBlur : Roo.emptyFn,
8008
8009     
8010     // private
8011     onBlur : function(){
8012         this.beforeBlur();
8013         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8014             //this.el.removeClass(this.focusClass);
8015         }
8016         this.hasFocus = false;
8017         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8018             this.validate();
8019         }
8020         var v = this.getValue();
8021         if(String(v) !== String(this.startValue)){
8022             this.fireEvent('change', this, v, this.startValue);
8023         }
8024         this.fireEvent("blur", this);
8025     },
8026     
8027     /**
8028      * Resets the current field value to the originally loaded value and clears any validation messages
8029      */
8030     reset : function(){
8031         this.setValue(this.originalValue);
8032         this.validate();
8033     },
8034      /**
8035      * Returns the name of the field
8036      * @return {Mixed} name The name field
8037      */
8038     getName: function(){
8039         return this.name;
8040     },
8041      /**
8042      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8043      * @return {Mixed} value The field value
8044      */
8045     getValue : function(){
8046         
8047         var v = this.inputEl().getValue();
8048         
8049         return v;
8050     },
8051     /**
8052      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8053      * @return {Mixed} value The field value
8054      */
8055     getRawValue : function(){
8056         var v = this.inputEl().getValue();
8057         
8058         return v;
8059     },
8060     
8061     /**
8062      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8063      * @param {Mixed} value The value to set
8064      */
8065     setRawValue : function(v){
8066         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8067     },
8068     
8069     selectText : function(start, end){
8070         var v = this.getRawValue();
8071         if(v.length > 0){
8072             start = start === undefined ? 0 : start;
8073             end = end === undefined ? v.length : end;
8074             var d = this.inputEl().dom;
8075             if(d.setSelectionRange){
8076                 d.setSelectionRange(start, end);
8077             }else if(d.createTextRange){
8078                 var range = d.createTextRange();
8079                 range.moveStart("character", start);
8080                 range.moveEnd("character", v.length-end);
8081                 range.select();
8082             }
8083         }
8084     },
8085     
8086     /**
8087      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8088      * @param {Mixed} value The value to set
8089      */
8090     setValue : function(v){
8091         this.value = v;
8092         if(this.rendered){
8093             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8094             this.validate();
8095         }
8096     },
8097     
8098     /*
8099     processValue : function(value){
8100         if(this.stripCharsRe){
8101             var newValue = value.replace(this.stripCharsRe, '');
8102             if(newValue !== value){
8103                 this.setRawValue(newValue);
8104                 return newValue;
8105             }
8106         }
8107         return value;
8108     },
8109   */
8110     preFocus : function(){
8111         
8112         if(this.selectOnFocus){
8113             this.inputEl().dom.select();
8114         }
8115     },
8116     filterKeys : function(e){
8117         var k = e.getKey();
8118         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8119             return;
8120         }
8121         var c = e.getCharCode(), cc = String.fromCharCode(c);
8122         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8123             return;
8124         }
8125         if(!this.maskRe.test(cc)){
8126             e.stopEvent();
8127         }
8128     },
8129      /**
8130      * Clear any invalid styles/messages for this field
8131      */
8132     clearInvalid : function(){
8133         
8134         if(!this.el || this.preventMark){ // not rendered
8135             return;
8136         }
8137         this.el.removeClass(this.invalidClass);
8138         
8139         this.fireEvent('valid', this);
8140     },
8141     
8142      /**
8143      * Mark this field as valid
8144      */
8145     markValid : function(){
8146         if(!this.el  || this.preventMark){ // not rendered
8147             return;
8148         }
8149         
8150         this.el.removeClass([this.invalidClass, this.validClass]);
8151         
8152         if(this.disabled || this.allowBlank){
8153             return;
8154         }
8155         
8156         this.el.addClass(this.validClass);
8157         
8158         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8159             
8160             var feedback = this.el.select('.form-control-feedback', true).first();
8161             
8162             if(feedback){
8163                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8164                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8165             }
8166             
8167         }
8168         
8169         this.fireEvent('valid', this);
8170     },
8171     
8172      /**
8173      * Mark this field as invalid
8174      * @param {String} msg The validation message
8175      */
8176     markInvalid : function(msg){
8177         if(!this.el  || this.preventMark){ // not rendered
8178             return;
8179         }
8180         
8181         this.el.removeClass([this.invalidClass, this.validClass]);
8182         
8183         if(this.disabled || this.allowBlank){
8184             return;
8185         }
8186         
8187         this.el.addClass(this.invalidClass);
8188         
8189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8190             
8191             var feedback = this.el.select('.form-control-feedback', true).first();
8192             
8193             if(feedback){
8194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8195                 
8196                 if(this.getValue().length){
8197                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8198                 }
8199                 
8200             }
8201             
8202         }
8203         
8204         this.fireEvent('invalid', this, msg);
8205     },
8206     // private
8207     SafariOnKeyDown : function(event)
8208     {
8209         // this is a workaround for a password hang bug on chrome/ webkit.
8210         
8211         var isSelectAll = false;
8212         
8213         if(this.inputEl().dom.selectionEnd > 0){
8214             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8215         }
8216         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8217             event.preventDefault();
8218             this.setValue('');
8219             return;
8220         }
8221         
8222         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8223             
8224             event.preventDefault();
8225             // this is very hacky as keydown always get's upper case.
8226             //
8227             var cc = String.fromCharCode(event.getCharCode());
8228             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8229             
8230         }
8231     },
8232     adjustWidth : function(tag, w){
8233         tag = tag.toLowerCase();
8234         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8235             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8236                 if(tag == 'input'){
8237                     return w + 2;
8238                 }
8239                 if(tag == 'textarea'){
8240                     return w-2;
8241                 }
8242             }else if(Roo.isOpera){
8243                 if(tag == 'input'){
8244                     return w + 2;
8245                 }
8246                 if(tag == 'textarea'){
8247                     return w-2;
8248                 }
8249             }
8250         }
8251         return w;
8252     }
8253     
8254 });
8255
8256  
8257 /*
8258  * - LGPL
8259  *
8260  * Input
8261  * 
8262  */
8263
8264 /**
8265  * @class Roo.bootstrap.TextArea
8266  * @extends Roo.bootstrap.Input
8267  * Bootstrap TextArea class
8268  * @cfg {Number} cols Specifies the visible width of a text area
8269  * @cfg {Number} rows Specifies the visible number of lines in a text area
8270  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8271  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8272  * @cfg {string} html text
8273  * 
8274  * @constructor
8275  * Create a new TextArea
8276  * @param {Object} config The config object
8277  */
8278
8279 Roo.bootstrap.TextArea = function(config){
8280     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8281    
8282 };
8283
8284 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8285      
8286     cols : false,
8287     rows : 5,
8288     readOnly : false,
8289     warp : 'soft',
8290     resize : false,
8291     value: false,
8292     html: false,
8293     
8294     getAutoCreate : function(){
8295         
8296         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8297         
8298         var id = Roo.id();
8299         
8300         var cfg = {};
8301         
8302         var input =  {
8303             tag: 'textarea',
8304             id : id,
8305             warp : this.warp,
8306             rows : this.rows,
8307             value : this.value || '',
8308             html: this.html || '',
8309             cls : 'form-control',
8310             placeholder : this.placeholder || '' 
8311             
8312         };
8313         
8314         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8315             input.maxLength = this.maxLength;
8316         }
8317         
8318         if(this.resize){
8319             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8320         }
8321         
8322         if(this.cols){
8323             input.cols = this.cols;
8324         }
8325         
8326         if (this.readOnly) {
8327             input.readonly = true;
8328         }
8329         
8330         if (this.name) {
8331             input.name = this.name;
8332         }
8333         
8334         if (this.size) {
8335             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8336         }
8337         
8338         var settings=this;
8339         ['xs','sm','md','lg'].map(function(size){
8340             if (settings[size]) {
8341                 cfg.cls += ' col-' + size + '-' + settings[size];
8342             }
8343         });
8344         
8345         var inputblock = input;
8346         
8347         if(this.hasFeedback && !this.allowBlank){
8348             
8349             var feedback = {
8350                 tag: 'span',
8351                 cls: 'glyphicon form-control-feedback'
8352             };
8353
8354             inputblock = {
8355                 cls : 'has-feedback',
8356                 cn :  [
8357                     input,
8358                     feedback
8359                 ] 
8360             };  
8361         }
8362         
8363         
8364         if (this.before || this.after) {
8365             
8366             inputblock = {
8367                 cls : 'input-group',
8368                 cn :  [] 
8369             };
8370             if (this.before) {
8371                 inputblock.cn.push({
8372                     tag :'span',
8373                     cls : 'input-group-addon',
8374                     html : this.before
8375                 });
8376             }
8377             
8378             inputblock.cn.push(input);
8379             
8380             if(this.hasFeedback && !this.allowBlank){
8381                 inputblock.cls += ' has-feedback';
8382                 inputblock.cn.push(feedback);
8383             }
8384             
8385             if (this.after) {
8386                 inputblock.cn.push({
8387                     tag :'span',
8388                     cls : 'input-group-addon',
8389                     html : this.after
8390                 });
8391             }
8392             
8393         }
8394         
8395         if (align ==='left' && this.fieldLabel.length) {
8396                 Roo.log("left and has label");
8397                 cfg.cn = [
8398                     
8399                     {
8400                         tag: 'label',
8401                         'for' :  id,
8402                         cls : 'control-label col-sm-' + this.labelWidth,
8403                         html : this.fieldLabel
8404                         
8405                     },
8406                     {
8407                         cls : "col-sm-" + (12 - this.labelWidth), 
8408                         cn: [
8409                             inputblock
8410                         ]
8411                     }
8412                     
8413                 ];
8414         } else if ( this.fieldLabel.length) {
8415                 Roo.log(" label");
8416                  cfg.cn = [
8417                    
8418                     {
8419                         tag: 'label',
8420                         //cls : 'input-group-addon',
8421                         html : this.fieldLabel
8422                         
8423                     },
8424                     
8425                     inputblock
8426                     
8427                 ];
8428
8429         } else {
8430             
8431                    Roo.log(" no label && no align");
8432                 cfg.cn = [
8433                     
8434                         inputblock
8435                     
8436                 ];
8437                 
8438                 
8439         }
8440         
8441         if (this.disabled) {
8442             input.disabled=true;
8443         }
8444         
8445         return cfg;
8446         
8447     },
8448     /**
8449      * return the real textarea element.
8450      */
8451     inputEl: function ()
8452     {
8453         return this.el.select('textarea.form-control',true).first();
8454     }
8455 });
8456
8457  
8458 /*
8459  * - LGPL
8460  *
8461  * trigger field - base class for combo..
8462  * 
8463  */
8464  
8465 /**
8466  * @class Roo.bootstrap.TriggerField
8467  * @extends Roo.bootstrap.Input
8468  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8469  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8470  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8471  * for which you can provide a custom implementation.  For example:
8472  * <pre><code>
8473 var trigger = new Roo.bootstrap.TriggerField();
8474 trigger.onTriggerClick = myTriggerFn;
8475 trigger.applyTo('my-field');
8476 </code></pre>
8477  *
8478  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8479  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8480  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8481  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8482  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8483
8484  * @constructor
8485  * Create a new TriggerField.
8486  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8487  * to the base TextField)
8488  */
8489 Roo.bootstrap.TriggerField = function(config){
8490     this.mimicing = false;
8491     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8492 };
8493
8494 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8495     /**
8496      * @cfg {String} triggerClass A CSS class to apply to the trigger
8497      */
8498      /**
8499      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8500      */
8501     hideTrigger:false,
8502
8503     /**
8504      * @cfg {Boolean} removable (true|false) special filter default false
8505      */
8506     removable : false,
8507     
8508     /** @cfg {Boolean} grow @hide */
8509     /** @cfg {Number} growMin @hide */
8510     /** @cfg {Number} growMax @hide */
8511
8512     /**
8513      * @hide 
8514      * @method
8515      */
8516     autoSize: Roo.emptyFn,
8517     // private
8518     monitorTab : true,
8519     // private
8520     deferHeight : true,
8521
8522     
8523     actionMode : 'wrap',
8524     
8525     caret : false,
8526     
8527     
8528     getAutoCreate : function(){
8529        
8530        Roo.log('run????????????????????');
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         
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.DISPALY).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     //private
11315     addicon : false,
11316     editicon: false,
11317     
11318     page: 0,
11319     hasQuery: false,
11320     append: false,
11321     loadNext: false,
11322     autoFocus : true,
11323     tickable : false,
11324     btnPosition : 'right',
11325     triggerList : true,
11326     showToggleBtn : true,
11327     animate : true,
11328     emptyResultText: 'Empty',
11329     // element that contains real text value.. (when hidden is used..)
11330     
11331     getAutoCreate : function()
11332     {
11333         var cfg = false;
11334         
11335         /*
11336          * Touch Devices
11337          */
11338         
11339         if(Roo.isTouch){
11340             cfg = this.getAutoCreateTouchView();
11341             return cfg;;
11342         }
11343         
11344         /*
11345          *  Normal ComboBox
11346          */
11347         if(!this.tickable){
11348             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11349             return cfg;
11350         }
11351         
11352         /*
11353          *  ComboBox with tickable selections
11354          */
11355              
11356         var align = this.labelAlign || this.parentLabelAlign();
11357         
11358         cfg = {
11359             cls : 'form-group roo-combobox-tickable' //input-group
11360         };
11361         
11362         var buttons = {
11363             tag : 'div',
11364             cls : 'tickable-buttons',
11365             cn : [
11366                 {
11367                     tag : 'button',
11368                     type : 'button',
11369                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11370                     html : 'Edit'
11371                 },
11372                 {
11373                     tag : 'button',
11374                     type : 'button',
11375                     name : 'ok',
11376                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11377                     html : 'Done'
11378                 },
11379                 {
11380                     tag : 'button',
11381                     type : 'button',
11382                     name : 'cancel',
11383                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11384                     html : 'Cancel'
11385                 }
11386             ]
11387         };
11388         
11389         if(this.editable){
11390             buttons.cn.unshift({
11391                 tag: 'input',
11392                 cls: 'select2-search-field-input'
11393             });
11394         }
11395         
11396         var _this = this;
11397         
11398         Roo.each(buttons.cn, function(c){
11399             if (_this.size) {
11400                 c.cls += ' btn-' + _this.size;
11401             }
11402
11403             if (_this.disabled) {
11404                 c.disabled = true;
11405             }
11406         });
11407         
11408         var box = {
11409             tag: 'div',
11410             cn: [
11411                 {
11412                     tag: 'input',
11413                     type : 'hidden',
11414                     cls: 'form-hidden-field'
11415                 },
11416                 {
11417                     tag: 'ul',
11418                     cls: 'select2-choices',
11419                     cn:[
11420                         {
11421                             tag: 'li',
11422                             cls: 'select2-search-field',
11423                             cn: [
11424
11425                                 buttons
11426                             ]
11427                         }
11428                     ]
11429                 }
11430             ]
11431         }
11432         
11433         var combobox = {
11434             cls: 'select2-container input-group select2-container-multi',
11435             cn: [
11436                 box
11437 //                {
11438 //                    tag: 'ul',
11439 //                    cls: 'typeahead typeahead-long dropdown-menu',
11440 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11441 //                }
11442             ]
11443         };
11444         
11445         if(this.hasFeedback && !this.allowBlank){
11446             
11447             var feedback = {
11448                 tag: 'span',
11449                 cls: 'glyphicon form-control-feedback'
11450             };
11451
11452             combobox.cn.push(feedback);
11453         }
11454         
11455         if (align ==='left' && this.fieldLabel.length) {
11456             
11457                 Roo.log("left and has label");
11458                 cfg.cn = [
11459                     
11460                     {
11461                         tag: 'label',
11462                         'for' :  id,
11463                         cls : 'control-label col-sm-' + this.labelWidth,
11464                         html : this.fieldLabel
11465                         
11466                     },
11467                     {
11468                         cls : "col-sm-" + (12 - this.labelWidth), 
11469                         cn: [
11470                             combobox
11471                         ]
11472                     }
11473                     
11474                 ];
11475         } else if ( this.fieldLabel.length) {
11476                 Roo.log(" label");
11477                  cfg.cn = [
11478                    
11479                     {
11480                         tag: 'label',
11481                         //cls : 'input-group-addon',
11482                         html : this.fieldLabel
11483                         
11484                     },
11485                     
11486                     combobox
11487                     
11488                 ];
11489
11490         } else {
11491             
11492                 Roo.log(" no label && no align");
11493                 cfg = combobox
11494                      
11495                 
11496         }
11497          
11498         var settings=this;
11499         ['xs','sm','md','lg'].map(function(size){
11500             if (settings[size]) {
11501                 cfg.cls += ' col-' + size + '-' + settings[size];
11502             }
11503         });
11504         
11505         return cfg;
11506         
11507     },
11508     
11509     // private
11510     initEvents: function()
11511     {
11512         
11513         if (!this.store) {
11514             throw "can not find store for combo";
11515         }
11516         
11517         this.store = Roo.factory(this.store, Roo.data);
11518         
11519         /*
11520          * Touch Devices
11521          */
11522         
11523         if(Roo.isTouch){
11524             this.initTouchView();
11525             return;
11526         }
11527         
11528         if(this.tickable){
11529             this.initTickableEvents();
11530             return;
11531         }
11532         
11533         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11534         
11535         if(this.hiddenName){
11536             
11537             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11538             
11539             this.hiddenField.dom.value =
11540                 this.hiddenValue !== undefined ? this.hiddenValue :
11541                 this.value !== undefined ? this.value : '';
11542
11543             // prevent input submission
11544             this.el.dom.removeAttribute('name');
11545             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11546              
11547              
11548         }
11549         //if(Roo.isGecko){
11550         //    this.el.dom.setAttribute('autocomplete', 'off');
11551         //}
11552         
11553         var cls = 'x-combo-list';
11554         
11555         //this.list = new Roo.Layer({
11556         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11557         //});
11558         
11559         var _this = this;
11560         
11561         (function(){
11562             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11563             _this.list.setWidth(lw);
11564         }).defer(100);
11565         
11566         this.list.on('mouseover', this.onViewOver, this);
11567         this.list.on('mousemove', this.onViewMove, this);
11568         
11569         this.list.on('scroll', this.onViewScroll, this);
11570         
11571         /*
11572         this.list.swallowEvent('mousewheel');
11573         this.assetHeight = 0;
11574
11575         if(this.title){
11576             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11577             this.assetHeight += this.header.getHeight();
11578         }
11579
11580         this.innerList = this.list.createChild({cls:cls+'-inner'});
11581         this.innerList.on('mouseover', this.onViewOver, this);
11582         this.innerList.on('mousemove', this.onViewMove, this);
11583         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11584         
11585         if(this.allowBlank && !this.pageSize && !this.disableClear){
11586             this.footer = this.list.createChild({cls:cls+'-ft'});
11587             this.pageTb = new Roo.Toolbar(this.footer);
11588            
11589         }
11590         if(this.pageSize){
11591             this.footer = this.list.createChild({cls:cls+'-ft'});
11592             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11593                     {pageSize: this.pageSize});
11594             
11595         }
11596         
11597         if (this.pageTb && this.allowBlank && !this.disableClear) {
11598             var _this = this;
11599             this.pageTb.add(new Roo.Toolbar.Fill(), {
11600                 cls: 'x-btn-icon x-btn-clear',
11601                 text: '&#160;',
11602                 handler: function()
11603                 {
11604                     _this.collapse();
11605                     _this.clearValue();
11606                     _this.onSelect(false, -1);
11607                 }
11608             });
11609         }
11610         if (this.footer) {
11611             this.assetHeight += this.footer.getHeight();
11612         }
11613         */
11614             
11615         if(!this.tpl){
11616             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11617         }
11618
11619         this.view = new Roo.View(this.list, this.tpl, {
11620             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11621         });
11622         //this.view.wrapEl.setDisplayed(false);
11623         this.view.on('click', this.onViewClick, this);
11624         
11625         
11626         
11627         this.store.on('beforeload', this.onBeforeLoad, this);
11628         this.store.on('load', this.onLoad, this);
11629         this.store.on('loadexception', this.onLoadException, this);
11630         /*
11631         if(this.resizable){
11632             this.resizer = new Roo.Resizable(this.list,  {
11633                pinned:true, handles:'se'
11634             });
11635             this.resizer.on('resize', function(r, w, h){
11636                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11637                 this.listWidth = w;
11638                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11639                 this.restrictHeight();
11640             }, this);
11641             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11642         }
11643         */
11644         if(!this.editable){
11645             this.editable = true;
11646             this.setEditable(false);
11647         }
11648         
11649         /*
11650         
11651         if (typeof(this.events.add.listeners) != 'undefined') {
11652             
11653             this.addicon = this.wrap.createChild(
11654                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11655        
11656             this.addicon.on('click', function(e) {
11657                 this.fireEvent('add', this);
11658             }, this);
11659         }
11660         if (typeof(this.events.edit.listeners) != 'undefined') {
11661             
11662             this.editicon = this.wrap.createChild(
11663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11664             if (this.addicon) {
11665                 this.editicon.setStyle('margin-left', '40px');
11666             }
11667             this.editicon.on('click', function(e) {
11668                 
11669                 // we fire even  if inothing is selected..
11670                 this.fireEvent('edit', this, this.lastData );
11671                 
11672             }, this);
11673         }
11674         */
11675         
11676         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11677             "up" : function(e){
11678                 this.inKeyMode = true;
11679                 this.selectPrev();
11680             },
11681
11682             "down" : function(e){
11683                 if(!this.isExpanded()){
11684                     this.onTriggerClick();
11685                 }else{
11686                     this.inKeyMode = true;
11687                     this.selectNext();
11688                 }
11689             },
11690
11691             "enter" : function(e){
11692 //                this.onViewClick();
11693                 //return true;
11694                 this.collapse();
11695                 
11696                 if(this.fireEvent("specialkey", this, e)){
11697                     this.onViewClick(false);
11698                 }
11699                 
11700                 return true;
11701             },
11702
11703             "esc" : function(e){
11704                 this.collapse();
11705             },
11706
11707             "tab" : function(e){
11708                 this.collapse();
11709                 
11710                 if(this.fireEvent("specialkey", this, e)){
11711                     this.onViewClick(false);
11712                 }
11713                 
11714                 return true;
11715             },
11716
11717             scope : this,
11718
11719             doRelay : function(foo, bar, hname){
11720                 if(hname == 'down' || this.scope.isExpanded()){
11721                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11722                 }
11723                 return true;
11724             },
11725
11726             forceKeyDown: true
11727         });
11728         
11729         
11730         this.queryDelay = Math.max(this.queryDelay || 10,
11731                 this.mode == 'local' ? 10 : 250);
11732         
11733         
11734         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11735         
11736         if(this.typeAhead){
11737             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11738         }
11739         if(this.editable !== false){
11740             this.inputEl().on("keyup", this.onKeyUp, this);
11741         }
11742         if(this.forceSelection){
11743             this.inputEl().on('blur', this.doForce, this);
11744         }
11745         
11746         if(this.multiple){
11747             this.choices = this.el.select('ul.select2-choices', true).first();
11748             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11749         }
11750     },
11751     
11752     initTickableEvents: function()
11753     {   
11754         this.createList();
11755         
11756         if(this.hiddenName){
11757             
11758             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11759             
11760             this.hiddenField.dom.value =
11761                 this.hiddenValue !== undefined ? this.hiddenValue :
11762                 this.value !== undefined ? this.value : '';
11763
11764             // prevent input submission
11765             this.el.dom.removeAttribute('name');
11766             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11767              
11768              
11769         }
11770         
11771 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11772         
11773         this.choices = this.el.select('ul.select2-choices', true).first();
11774         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11775         if(this.triggerList){
11776             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11777         }
11778          
11779         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11780         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11781         
11782         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11783         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11784         
11785         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11786         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11787         
11788         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11789         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11790         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11791         
11792         this.okBtn.hide();
11793         this.cancelBtn.hide();
11794         
11795         var _this = this;
11796         
11797         (function(){
11798             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11799             _this.list.setWidth(lw);
11800         }).defer(100);
11801         
11802         this.list.on('mouseover', this.onViewOver, this);
11803         this.list.on('mousemove', this.onViewMove, this);
11804         
11805         this.list.on('scroll', this.onViewScroll, this);
11806         
11807         if(!this.tpl){
11808             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>';
11809         }
11810
11811         this.view = new Roo.View(this.list, this.tpl, {
11812             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11813         });
11814         
11815         //this.view.wrapEl.setDisplayed(false);
11816         this.view.on('click', this.onViewClick, this);
11817         
11818         
11819         
11820         this.store.on('beforeload', this.onBeforeLoad, this);
11821         this.store.on('load', this.onLoad, this);
11822         this.store.on('loadexception', this.onLoadException, this);
11823         
11824         if(this.editable){
11825             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11826                 "up" : function(e){
11827                     this.inKeyMode = true;
11828                     this.selectPrev();
11829                 },
11830
11831                 "down" : function(e){
11832                     this.inKeyMode = true;
11833                     this.selectNext();
11834                 },
11835
11836                 "enter" : function(e){
11837                     if(this.fireEvent("specialkey", this, e)){
11838                         this.onViewClick(false);
11839                     }
11840                     
11841                     return true;
11842                 },
11843
11844                 "esc" : function(e){
11845                     this.onTickableFooterButtonClick(e, false, false);
11846                 },
11847
11848                 "tab" : function(e){
11849                     this.fireEvent("specialkey", this, e);
11850                     
11851                     this.onTickableFooterButtonClick(e, false, false);
11852                     
11853                     return true;
11854                 },
11855
11856                 scope : this,
11857
11858                 doRelay : function(e, fn, key){
11859                     if(this.scope.isExpanded()){
11860                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11861                     }
11862                     return true;
11863                 },
11864
11865                 forceKeyDown: true
11866             });
11867         }
11868         
11869         this.queryDelay = Math.max(this.queryDelay || 10,
11870                 this.mode == 'local' ? 10 : 250);
11871         
11872         
11873         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11874         
11875         if(this.typeAhead){
11876             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11877         }
11878         
11879         if(this.editable !== false){
11880             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11881         }
11882         
11883     },
11884
11885     onDestroy : function(){
11886         if(this.view){
11887             this.view.setStore(null);
11888             this.view.el.removeAllListeners();
11889             this.view.el.remove();
11890             this.view.purgeListeners();
11891         }
11892         if(this.list){
11893             this.list.dom.innerHTML  = '';
11894         }
11895         
11896         if(this.store){
11897             this.store.un('beforeload', this.onBeforeLoad, this);
11898             this.store.un('load', this.onLoad, this);
11899             this.store.un('loadexception', this.onLoadException, this);
11900         }
11901         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11902     },
11903
11904     // private
11905     fireKey : function(e){
11906         if(e.isNavKeyPress() && !this.list.isVisible()){
11907             this.fireEvent("specialkey", this, e);
11908         }
11909     },
11910
11911     // private
11912     onResize: function(w, h){
11913 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11914 //        
11915 //        if(typeof w != 'number'){
11916 //            // we do not handle it!?!?
11917 //            return;
11918 //        }
11919 //        var tw = this.trigger.getWidth();
11920 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11921 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11922 //        var x = w - tw;
11923 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11924 //            
11925 //        //this.trigger.setStyle('left', x+'px');
11926 //        
11927 //        if(this.list && this.listWidth === undefined){
11928 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11929 //            this.list.setWidth(lw);
11930 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11931 //        }
11932         
11933     
11934         
11935     },
11936
11937     /**
11938      * Allow or prevent the user from directly editing the field text.  If false is passed,
11939      * the user will only be able to select from the items defined in the dropdown list.  This method
11940      * is the runtime equivalent of setting the 'editable' config option at config time.
11941      * @param {Boolean} value True to allow the user to directly edit the field text
11942      */
11943     setEditable : function(value){
11944         if(value == this.editable){
11945             return;
11946         }
11947         this.editable = value;
11948         if(!value){
11949             this.inputEl().dom.setAttribute('readOnly', true);
11950             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11951             this.inputEl().addClass('x-combo-noedit');
11952         }else{
11953             this.inputEl().dom.setAttribute('readOnly', false);
11954             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11955             this.inputEl().removeClass('x-combo-noedit');
11956         }
11957     },
11958
11959     // private
11960     
11961     onBeforeLoad : function(combo,opts){
11962         if(!this.hasFocus){
11963             return;
11964         }
11965          if (!opts.add) {
11966             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11967          }
11968         this.restrictHeight();
11969         this.selectedIndex = -1;
11970     },
11971
11972     // private
11973     onLoad : function(){
11974         
11975         this.hasQuery = false;
11976         
11977         if(!this.hasFocus){
11978             return;
11979         }
11980         
11981         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11982             this.loading.hide();
11983         }
11984              
11985         if(this.store.getCount() > 0){
11986             this.expand();
11987             this.restrictHeight();
11988             if(this.lastQuery == this.allQuery){
11989                 if(this.editable && !this.tickable){
11990                     this.inputEl().dom.select();
11991                 }
11992                 
11993                 if(
11994                     !this.selectByValue(this.value, true) &&
11995                     this.autoFocus && 
11996                     (
11997                         !this.store.lastOptions ||
11998                         typeof(this.store.lastOptions.add) == 'undefined' || 
11999                         this.store.lastOptions.add != true
12000                     )
12001                 ){
12002                     this.select(0, true);
12003                 }
12004             }else{
12005                 if(this.autoFocus){
12006                     this.selectNext();
12007                 }
12008                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12009                     this.taTask.delay(this.typeAheadDelay);
12010                 }
12011             }
12012         }else{
12013             this.onEmptyResults();
12014         }
12015         
12016         //this.el.focus();
12017     },
12018     // private
12019     onLoadException : function()
12020     {
12021         this.hasQuery = false;
12022         
12023         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12024             this.loading.hide();
12025         }
12026         
12027         if(this.tickable && this.editable){
12028             return;
12029         }
12030         
12031         this.collapse();
12032         
12033         Roo.log(this.store.reader.jsonData);
12034         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12035             // fixme
12036             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12037         }
12038         
12039         
12040     },
12041     // private
12042     onTypeAhead : function(){
12043         if(this.store.getCount() > 0){
12044             var r = this.store.getAt(0);
12045             var newValue = r.data[this.displayField];
12046             var len = newValue.length;
12047             var selStart = this.getRawValue().length;
12048             
12049             if(selStart != len){
12050                 this.setRawValue(newValue);
12051                 this.selectText(selStart, newValue.length);
12052             }
12053         }
12054     },
12055
12056     // private
12057     onSelect : function(record, index){
12058         
12059         if(this.fireEvent('beforeselect', this, record, index) !== false){
12060         
12061             this.setFromData(index > -1 ? record.data : false);
12062             
12063             this.collapse();
12064             this.fireEvent('select', this, record, index);
12065         }
12066     },
12067
12068     /**
12069      * Returns the currently selected field value or empty string if no value is set.
12070      * @return {String} value The selected value
12071      */
12072     getValue : function(){
12073         
12074         if(this.multiple){
12075             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12076         }
12077         
12078         if(this.valueField){
12079             return typeof this.value != 'undefined' ? this.value : '';
12080         }else{
12081             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12082         }
12083     },
12084
12085     /**
12086      * Clears any text/value currently set in the field
12087      */
12088     clearValue : function(){
12089         if(this.hiddenField){
12090             this.hiddenField.dom.value = '';
12091         }
12092         this.value = '';
12093         this.setRawValue('');
12094         this.lastSelectionText = '';
12095         this.lastData = false;
12096         
12097         var close = this.closeTriggerEl();
12098         
12099         if(close){
12100             close.hide();
12101         }
12102         
12103     },
12104
12105     /**
12106      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12107      * will be displayed in the field.  If the value does not match the data value of an existing item,
12108      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12109      * Otherwise the field will be blank (although the value will still be set).
12110      * @param {String} value The value to match
12111      */
12112     setValue : function(v){
12113         if(this.multiple){
12114             this.syncValue();
12115             return;
12116         }
12117         
12118         var text = v;
12119         if(this.valueField){
12120             var r = this.findRecord(this.valueField, v);
12121             if(r){
12122                 text = r.data[this.displayField];
12123             }else if(this.valueNotFoundText !== undefined){
12124                 text = this.valueNotFoundText;
12125             }
12126         }
12127         this.lastSelectionText = text;
12128         if(this.hiddenField){
12129             this.hiddenField.dom.value = v;
12130         }
12131         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12132         this.value = v;
12133         
12134         var close = this.closeTriggerEl();
12135         
12136         if(close){
12137             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12138         }
12139     },
12140     /**
12141      * @property {Object} the last set data for the element
12142      */
12143     
12144     lastData : false,
12145     /**
12146      * Sets the value of the field based on a object which is related to the record format for the store.
12147      * @param {Object} value the value to set as. or false on reset?
12148      */
12149     setFromData : function(o){
12150         
12151         if(this.multiple){
12152             this.addItem(o);
12153             return;
12154         }
12155             
12156         var dv = ''; // display value
12157         var vv = ''; // value value..
12158         this.lastData = o;
12159         if (this.displayField) {
12160             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12161         } else {
12162             // this is an error condition!!!
12163             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12164         }
12165         
12166         if(this.valueField){
12167             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12168         }
12169         
12170         var close = this.closeTriggerEl();
12171         
12172         if(close){
12173             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12174         }
12175         
12176         if(this.hiddenField){
12177             this.hiddenField.dom.value = vv;
12178             
12179             this.lastSelectionText = dv;
12180             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12181             this.value = vv;
12182             return;
12183         }
12184         // no hidden field.. - we store the value in 'value', but still display
12185         // display field!!!!
12186         this.lastSelectionText = dv;
12187         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12188         this.value = vv;
12189         
12190         
12191         
12192     },
12193     // private
12194     reset : function(){
12195         // overridden so that last data is reset..
12196         
12197         if(this.multiple){
12198             this.clearItem();
12199             return;
12200         }
12201         
12202         this.setValue(this.originalValue);
12203         this.clearInvalid();
12204         this.lastData = false;
12205         if (this.view) {
12206             this.view.clearSelections();
12207         }
12208     },
12209     // private
12210     findRecord : function(prop, value){
12211         var record;
12212         if(this.store.getCount() > 0){
12213             this.store.each(function(r){
12214                 if(r.data[prop] == value){
12215                     record = r;
12216                     return false;
12217                 }
12218                 return true;
12219             });
12220         }
12221         return record;
12222     },
12223     
12224     getName: function()
12225     {
12226         // returns hidden if it's set..
12227         if (!this.rendered) {return ''};
12228         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12229         
12230     },
12231     // private
12232     onViewMove : function(e, t){
12233         this.inKeyMode = false;
12234     },
12235
12236     // private
12237     onViewOver : function(e, t){
12238         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12239             return;
12240         }
12241         var item = this.view.findItemFromChild(t);
12242         
12243         if(item){
12244             var index = this.view.indexOf(item);
12245             this.select(index, false);
12246         }
12247     },
12248
12249     // private
12250     onViewClick : function(view, doFocus, el, e)
12251     {
12252         var index = this.view.getSelectedIndexes()[0];
12253         
12254         var r = this.store.getAt(index);
12255         
12256         if(this.tickable){
12257             
12258             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12259                 return;
12260             }
12261             
12262             var rm = false;
12263             var _this = this;
12264             
12265             Roo.each(this.tickItems, function(v,k){
12266                 
12267                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12268                     _this.tickItems.splice(k, 1);
12269                     
12270                     if(typeof(e) == 'undefined' && view == false){
12271                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12272                     }
12273                     
12274                     rm = true;
12275                     return;
12276                 }
12277             });
12278             
12279             if(rm){
12280                 return;
12281             }
12282             
12283             this.tickItems.push(r.data);
12284             
12285             if(typeof(e) == 'undefined' && view == false){
12286                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12287             }
12288                     
12289             return;
12290         }
12291         
12292         if(r){
12293             this.onSelect(r, index);
12294         }
12295         if(doFocus !== false && !this.blockFocus){
12296             this.inputEl().focus();
12297         }
12298     },
12299
12300     // private
12301     restrictHeight : function(){
12302         //this.innerList.dom.style.height = '';
12303         //var inner = this.innerList.dom;
12304         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12305         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12306         //this.list.beginUpdate();
12307         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12308         this.list.alignTo(this.inputEl(), this.listAlign);
12309         this.list.alignTo(this.inputEl(), this.listAlign);
12310         //this.list.endUpdate();
12311     },
12312
12313     // private
12314     onEmptyResults : function(){
12315         
12316         if(this.tickable && this.editable){
12317             this.restrictHeight();
12318             return;
12319         }
12320         
12321         this.collapse();
12322     },
12323
12324     /**
12325      * Returns true if the dropdown list is expanded, else false.
12326      */
12327     isExpanded : function(){
12328         return this.list.isVisible();
12329     },
12330
12331     /**
12332      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12334      * @param {String} value The data value of the item to select
12335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12336      * selected item if it is not currently in view (defaults to true)
12337      * @return {Boolean} True if the value matched an item in the list, else false
12338      */
12339     selectByValue : function(v, scrollIntoView){
12340         if(v !== undefined && v !== null){
12341             var r = this.findRecord(this.valueField || this.displayField, v);
12342             if(r){
12343                 this.select(this.store.indexOf(r), scrollIntoView);
12344                 return true;
12345             }
12346         }
12347         return false;
12348     },
12349
12350     /**
12351      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12352      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12353      * @param {Number} index The zero-based index of the list item to select
12354      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12355      * selected item if it is not currently in view (defaults to true)
12356      */
12357     select : function(index, scrollIntoView){
12358         this.selectedIndex = index;
12359         this.view.select(index);
12360         if(scrollIntoView !== false){
12361             var el = this.view.getNode(index);
12362             /*
12363              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12364              */
12365             if(el){
12366                 this.list.scrollChildIntoView(el, false);
12367             }
12368         }
12369     },
12370
12371     // private
12372     selectNext : function(){
12373         var ct = this.store.getCount();
12374         if(ct > 0){
12375             if(this.selectedIndex == -1){
12376                 this.select(0);
12377             }else if(this.selectedIndex < ct-1){
12378                 this.select(this.selectedIndex+1);
12379             }
12380         }
12381     },
12382
12383     // private
12384     selectPrev : function(){
12385         var ct = this.store.getCount();
12386         if(ct > 0){
12387             if(this.selectedIndex == -1){
12388                 this.select(0);
12389             }else if(this.selectedIndex != 0){
12390                 this.select(this.selectedIndex-1);
12391             }
12392         }
12393     },
12394
12395     // private
12396     onKeyUp : function(e){
12397         if(this.editable !== false && !e.isSpecialKey()){
12398             this.lastKey = e.getKey();
12399             this.dqTask.delay(this.queryDelay);
12400         }
12401     },
12402
12403     // private
12404     validateBlur : function(){
12405         return !this.list || !this.list.isVisible();   
12406     },
12407
12408     // private
12409     initQuery : function(){
12410         
12411         var v = this.getRawValue();
12412         
12413         if(this.tickable && this.editable){
12414             v = this.tickableInputEl().getValue();
12415         }
12416         
12417         this.doQuery(v);
12418     },
12419
12420     // private
12421     doForce : function(){
12422         if(this.inputEl().dom.value.length > 0){
12423             this.inputEl().dom.value =
12424                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12425              
12426         }
12427     },
12428
12429     /**
12430      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12431      * query allowing the query action to be canceled if needed.
12432      * @param {String} query The SQL query to execute
12433      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12434      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12435      * saved in the current store (defaults to false)
12436      */
12437     doQuery : function(q, forceAll){
12438         
12439         if(q === undefined || q === null){
12440             q = '';
12441         }
12442         var qe = {
12443             query: q,
12444             forceAll: forceAll,
12445             combo: this,
12446             cancel:false
12447         };
12448         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12449             return false;
12450         }
12451         q = qe.query;
12452         
12453         forceAll = qe.forceAll;
12454         if(forceAll === true || (q.length >= this.minChars)){
12455             
12456             this.hasQuery = true;
12457             
12458             if(this.lastQuery != q || this.alwaysQuery){
12459                 this.lastQuery = q;
12460                 if(this.mode == 'local'){
12461                     this.selectedIndex = -1;
12462                     if(forceAll){
12463                         this.store.clearFilter();
12464                     }else{
12465                         
12466                         if(this.specialFilter){
12467                             this.fireEvent('specialfilter', this);
12468                             this.onLoad();
12469                             return;
12470                         }
12471                         
12472                         this.store.filter(this.displayField, q);
12473                     }
12474                     
12475                     this.store.fireEvent("datachanged", this.store);
12476                     
12477                     this.onLoad();
12478                     
12479                     
12480                 }else{
12481                     
12482                     this.store.baseParams[this.queryParam] = q;
12483                     
12484                     var options = {params : this.getParams(q)};
12485                     
12486                     if(this.loadNext){
12487                         options.add = true;
12488                         options.params.start = this.page * this.pageSize;
12489                     }
12490                     
12491                     this.store.load(options);
12492                     
12493                     /*
12494                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12495                      *  we should expand the list on onLoad
12496                      *  so command out it
12497                      */
12498 //                    this.expand();
12499                 }
12500             }else{
12501                 this.selectedIndex = -1;
12502                 this.onLoad();   
12503             }
12504         }
12505         
12506         this.loadNext = false;
12507     },
12508     
12509     // private
12510     getParams : function(q){
12511         var p = {};
12512         //p[this.queryParam] = q;
12513         
12514         if(this.pageSize){
12515             p.start = 0;
12516             p.limit = this.pageSize;
12517         }
12518         return p;
12519     },
12520
12521     /**
12522      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12523      */
12524     collapse : function(){
12525         if(!this.isExpanded()){
12526             return;
12527         }
12528         
12529         this.list.hide();
12530         
12531         if(this.tickable){
12532             this.hasFocus = false;
12533             this.okBtn.hide();
12534             this.cancelBtn.hide();
12535             this.trigger.show();
12536             
12537             if(this.editable){
12538                 this.tickableInputEl().dom.value = '';
12539                 this.tickableInputEl().blur();
12540             }
12541             
12542         }
12543         
12544         Roo.get(document).un('mousedown', this.collapseIf, this);
12545         Roo.get(document).un('mousewheel', this.collapseIf, this);
12546         if (!this.editable) {
12547             Roo.get(document).un('keydown', this.listKeyPress, this);
12548         }
12549         this.fireEvent('collapse', this);
12550     },
12551
12552     // private
12553     collapseIf : function(e){
12554         var in_combo  = e.within(this.el);
12555         var in_list =  e.within(this.list);
12556         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12557         
12558         if (in_combo || in_list || is_list) {
12559             //e.stopPropagation();
12560             return;
12561         }
12562         
12563         if(this.tickable){
12564             this.onTickableFooterButtonClick(e, false, false);
12565         }
12566
12567         this.collapse();
12568         
12569     },
12570
12571     /**
12572      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12573      */
12574     expand : function(){
12575        
12576         if(this.isExpanded() || !this.hasFocus){
12577             return;
12578         }
12579         
12580         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12581         this.list.setWidth(lw);
12582         
12583         
12584          Roo.log('expand');
12585         
12586         this.list.show();
12587         
12588         this.restrictHeight();
12589         
12590         if(this.tickable){
12591             
12592             this.tickItems = Roo.apply([], this.item);
12593             
12594             this.okBtn.show();
12595             this.cancelBtn.show();
12596             this.trigger.hide();
12597             
12598             if(this.editable){
12599                 this.tickableInputEl().focus();
12600             }
12601             
12602         }
12603         
12604         Roo.get(document).on('mousedown', this.collapseIf, this);
12605         Roo.get(document).on('mousewheel', this.collapseIf, this);
12606         if (!this.editable) {
12607             Roo.get(document).on('keydown', this.listKeyPress, this);
12608         }
12609         
12610         this.fireEvent('expand', this);
12611     },
12612
12613     // private
12614     // Implements the default empty TriggerField.onTriggerClick function
12615     onTriggerClick : function(e)
12616     {
12617         Roo.log('trigger click');
12618         
12619         if(this.disabled || !this.triggerList){
12620             return;
12621         }
12622         
12623         this.page = 0;
12624         this.loadNext = false;
12625         
12626         if(this.isExpanded()){
12627             this.collapse();
12628             if (!this.blockFocus) {
12629                 this.inputEl().focus();
12630             }
12631             
12632         }else {
12633             this.hasFocus = true;
12634             if(this.triggerAction == 'all') {
12635                 this.doQuery(this.allQuery, true);
12636             } else {
12637                 this.doQuery(this.getRawValue());
12638             }
12639             if (!this.blockFocus) {
12640                 this.inputEl().focus();
12641             }
12642         }
12643     },
12644     
12645     onTickableTriggerClick : function(e)
12646     {
12647         if(this.disabled){
12648             return;
12649         }
12650         
12651         this.page = 0;
12652         this.loadNext = false;
12653         this.hasFocus = true;
12654         
12655         if(this.triggerAction == 'all') {
12656             this.doQuery(this.allQuery, true);
12657         } else {
12658             this.doQuery(this.getRawValue());
12659         }
12660     },
12661     
12662     onSearchFieldClick : function(e)
12663     {
12664         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12665             this.onTickableFooterButtonClick(e, false, false);
12666             return;
12667         }
12668         
12669         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12670             return;
12671         }
12672         
12673         this.page = 0;
12674         this.loadNext = false;
12675         this.hasFocus = true;
12676         
12677         if(this.triggerAction == 'all') {
12678             this.doQuery(this.allQuery, true);
12679         } else {
12680             this.doQuery(this.getRawValue());
12681         }
12682     },
12683     
12684     listKeyPress : function(e)
12685     {
12686         //Roo.log('listkeypress');
12687         // scroll to first matching element based on key pres..
12688         if (e.isSpecialKey()) {
12689             return false;
12690         }
12691         var k = String.fromCharCode(e.getKey()).toUpperCase();
12692         //Roo.log(k);
12693         var match  = false;
12694         var csel = this.view.getSelectedNodes();
12695         var cselitem = false;
12696         if (csel.length) {
12697             var ix = this.view.indexOf(csel[0]);
12698             cselitem  = this.store.getAt(ix);
12699             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12700                 cselitem = false;
12701             }
12702             
12703         }
12704         
12705         this.store.each(function(v) { 
12706             if (cselitem) {
12707                 // start at existing selection.
12708                 if (cselitem.id == v.id) {
12709                     cselitem = false;
12710                 }
12711                 return true;
12712             }
12713                 
12714             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12715                 match = this.store.indexOf(v);
12716                 return false;
12717             }
12718             return true;
12719         }, this);
12720         
12721         if (match === false) {
12722             return true; // no more action?
12723         }
12724         // scroll to?
12725         this.view.select(match);
12726         var sn = Roo.get(this.view.getSelectedNodes()[0])
12727         sn.scrollIntoView(sn.dom.parentNode, false);
12728     },
12729     
12730     onViewScroll : function(e, t){
12731         
12732         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){
12733             return;
12734         }
12735         
12736         this.hasQuery = true;
12737         
12738         this.loading = this.list.select('.loading', true).first();
12739         
12740         if(this.loading === null){
12741             this.list.createChild({
12742                 tag: 'div',
12743                 cls: 'loading select2-more-results select2-active',
12744                 html: 'Loading more results...'
12745             })
12746             
12747             this.loading = this.list.select('.loading', true).first();
12748             
12749             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12750             
12751             this.loading.hide();
12752         }
12753         
12754         this.loading.show();
12755         
12756         var _combo = this;
12757         
12758         this.page++;
12759         this.loadNext = true;
12760         
12761         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12762         
12763         return;
12764     },
12765     
12766     addItem : function(o)
12767     {   
12768         var dv = ''; // display value
12769         
12770         if (this.displayField) {
12771             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12772         } else {
12773             // this is an error condition!!!
12774             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12775         }
12776         
12777         if(!dv.length){
12778             return;
12779         }
12780         
12781         var choice = this.choices.createChild({
12782             tag: 'li',
12783             cls: 'select2-search-choice',
12784             cn: [
12785                 {
12786                     tag: 'div',
12787                     html: dv
12788                 },
12789                 {
12790                     tag: 'a',
12791                     href: '#',
12792                     cls: 'select2-search-choice-close',
12793                     tabindex: '-1'
12794                 }
12795             ]
12796             
12797         }, this.searchField);
12798         
12799         var close = choice.select('a.select2-search-choice-close', true).first()
12800         
12801         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12802         
12803         this.item.push(o);
12804         
12805         this.lastData = o;
12806         
12807         this.syncValue();
12808         
12809         this.inputEl().dom.value = '';
12810         
12811         this.validate();
12812     },
12813     
12814     onRemoveItem : function(e, _self, o)
12815     {
12816         e.preventDefault();
12817         
12818         this.lastItem = Roo.apply([], this.item);
12819         
12820         var index = this.item.indexOf(o.data) * 1;
12821         
12822         if( index < 0){
12823             Roo.log('not this item?!');
12824             return;
12825         }
12826         
12827         this.item.splice(index, 1);
12828         o.item.remove();
12829         
12830         this.syncValue();
12831         
12832         this.fireEvent('remove', this, e);
12833         
12834         this.validate();
12835         
12836     },
12837     
12838     syncValue : function()
12839     {
12840         if(!this.item.length){
12841             this.clearValue();
12842             return;
12843         }
12844             
12845         var value = [];
12846         var _this = this;
12847         Roo.each(this.item, function(i){
12848             if(_this.valueField){
12849                 value.push(i[_this.valueField]);
12850                 return;
12851             }
12852
12853             value.push(i);
12854         });
12855
12856         this.value = value.join(',');
12857
12858         if(this.hiddenField){
12859             this.hiddenField.dom.value = this.value;
12860         }
12861         
12862         this.store.fireEvent("datachanged", this.store);
12863     },
12864     
12865     clearItem : function()
12866     {
12867         if(!this.multiple){
12868             return;
12869         }
12870         
12871         this.item = [];
12872         
12873         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12874            c.remove();
12875         });
12876         
12877         this.syncValue();
12878         
12879         this.validate();
12880     },
12881     
12882     inputEl: function ()
12883     {
12884         if(this.tickable && !Roo.isTouch){
12885             return this.searchField;
12886         }
12887         return this.el.select('input.form-control',true).first();
12888     },
12889     
12890     
12891     onTickableFooterButtonClick : function(e, btn, el)
12892     {
12893         e.preventDefault();
12894         
12895         this.lastItem = Roo.apply([], this.item);
12896         
12897         if(btn && btn.name == 'cancel'){
12898             this.tickItems = Roo.apply([], this.item);
12899             this.collapse();
12900             return;
12901         }
12902         
12903         this.clearItem();
12904         
12905         var _this = this;
12906         
12907         Roo.each(this.tickItems, function(o){
12908             _this.addItem(o);
12909         });
12910         
12911         this.collapse();
12912         
12913     },
12914     
12915     validate : function()
12916     {
12917         var v = this.getRawValue();
12918         
12919         if(this.multiple){
12920             v = this.getValue();
12921         }
12922         
12923         if(this.disabled || this.allowBlank || v.length){
12924             this.markValid();
12925             return true;
12926         }
12927         
12928         this.markInvalid();
12929         return false;
12930     },
12931     
12932     tickableInputEl : function()
12933     {
12934         if(!this.tickable || !this.editable){
12935             return this.inputEl();
12936         }
12937         
12938         return this.inputEl().select('.select2-search-field-input', true).first();
12939     },
12940     
12941     
12942     getAutoCreateTouchView : function()
12943     {
12944         var id = Roo.id();
12945         
12946         var cfg = {
12947             cls: 'form-group' //input-group
12948         };
12949         
12950         var input =  {
12951             tag: 'input',
12952             id : id,
12953             type : this.inputType,
12954             cls : 'form-control x-combo-noedit',
12955             autocomplete: 'new-password',
12956             placeholder : this.placeholder || '',
12957             readonly : true
12958         };
12959         
12960         if (this.name) {
12961             input.name = this.name;
12962         }
12963         
12964         if (this.size) {
12965             input.cls += ' input-' + this.size;
12966         }
12967         
12968         if (this.disabled) {
12969             input.disabled = true;
12970         }
12971         
12972         var inputblock = {
12973             cls : '',
12974             cn : [
12975                 input
12976             ]
12977         };
12978         
12979         if(this.before){
12980             inputblock.cls += ' input-group';
12981             
12982             inputblock.cn.unshift({
12983                 tag :'span',
12984                 cls : 'input-group-addon',
12985                 html : this.before
12986             });
12987         }
12988         
12989         if(this.removable && !this.multiple){
12990             inputblock.cls += ' roo-removable';
12991             
12992             inputblock.cn.push({
12993                 tag: 'button',
12994                 html : 'x',
12995                 cls : 'roo-combo-removable-btn close'
12996             });
12997         }
12998
12999         if(this.hasFeedback && !this.allowBlank){
13000             
13001             inputblock.cls += ' has-feedback';
13002             
13003             inputblock.cn.push({
13004                 tag: 'span',
13005                 cls: 'glyphicon form-control-feedback'
13006             });
13007             
13008         }
13009         
13010         if (this.after) {
13011             
13012             inputblock.cls += (this.before) ? '' : ' input-group';
13013             
13014             inputblock.cn.push({
13015                 tag :'span',
13016                 cls : 'input-group-addon',
13017                 html : this.after
13018             });
13019         }
13020
13021         var box = {
13022             tag: 'div',
13023             cn: [
13024                 {
13025                     tag: 'input',
13026                     type : 'hidden',
13027                     cls: 'form-hidden-field'
13028                 },
13029                 inputblock
13030             ]
13031             
13032         };
13033         
13034         if(this.multiple){
13035             box = {
13036                 tag: 'div',
13037                 cn: [
13038                     {
13039                         tag: 'input',
13040                         type : 'hidden',
13041                         cls: 'form-hidden-field'
13042                     },
13043                     {
13044                         tag: 'ul',
13045                         cls: 'select2-choices',
13046                         cn:[
13047                             {
13048                                 tag: 'li',
13049                                 cls: 'select2-search-field',
13050                                 cn: [
13051
13052                                     inputblock
13053                                 ]
13054                             }
13055                         ]
13056                     }
13057                 ]
13058             }
13059         };
13060         
13061         var combobox = {
13062             cls: 'select2-container input-group',
13063             cn: [
13064                 box
13065             ]
13066         };
13067         
13068         if(this.multiple){
13069             combobox.cls += ' select2-container-multi';
13070         }
13071         
13072         var align = this.labelAlign || this.parentLabelAlign();
13073         
13074         cfg.cn = combobox;
13075         
13076         if(this.fieldLabel.length){
13077             
13078             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13079             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13080             
13081             cfg.cn = [
13082                 {
13083                     tag: 'label',
13084                     cls : 'control-label ' + lw,
13085                     html : this.fieldLabel
13086
13087                 },
13088                 {
13089                     cls : cw, 
13090                     cn: [
13091                         combobox
13092                     ]
13093                 }
13094             ];
13095         }
13096         
13097         var settings = this;
13098         
13099         ['xs','sm','md','lg'].map(function(size){
13100             if (settings[size]) {
13101                 cfg.cls += ' col-' + size + '-' + settings[size];
13102             }
13103         });
13104         
13105         return cfg;
13106     },
13107     
13108     initTouchView : function()
13109     {
13110         this.renderTouchView();
13111         
13112         this.inputEl().on("click", this.showTouchView, this);
13113         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13114         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13115         
13116         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13117         
13118         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13119         this.store.on('load', this.onTouchViewLoad, this);
13120         this.store.on('loadexception', this.onTouchViewLoadException, this);
13121         
13122         if(this.hiddenName){
13123             
13124             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13125             
13126             this.hiddenField.dom.value =
13127                 this.hiddenValue !== undefined ? this.hiddenValue :
13128                 this.value !== undefined ? this.value : '';
13129         
13130             this.el.dom.removeAttribute('name');
13131             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13132         }
13133         
13134         if(this.multiple){
13135             this.choices = this.el.select('ul.select2-choices', true).first();
13136             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13137         }
13138         
13139         if(this.removable && !this.multiple){
13140             var close = this.closeTriggerEl();
13141             
13142             if(close){
13143                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
13144                 close.on('click', this.removeBtnClick, this, close);
13145             }
13146         }
13147         
13148         return;
13149         
13150         
13151     },
13152     
13153     renderTouchView : function()
13154     {
13155         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13156         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13157         
13158         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13159         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13160         
13161         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13162         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13163         this.touchViewBodyEl.setStyle('overflow', 'auto');
13164         
13165         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13166         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13167         
13168         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13169         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13170         
13171     },
13172     
13173     showTouchView : function()
13174     {
13175         this.touchViewHeaderEl.hide();
13176
13177         if(this.fieldLabel.length){
13178             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13179             this.touchViewHeaderEl.show();
13180         }
13181
13182         this.touchViewEl.show();
13183
13184         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13185         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13186
13187         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13188
13189         if(this.fieldLabel.length){
13190             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13191         }
13192
13193         this.touchViewBodyEl.setHeight(bodyHeight);
13194
13195         if(this.animate){
13196             var _this = this;
13197             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13198         }else{
13199             this.touchViewEl.addClass('in');
13200         }
13201
13202         this.doTouchViewQuery();
13203         
13204     },
13205     
13206     hideTouchView : function()
13207     {
13208         this.touchViewEl.removeClass('in');
13209
13210         if(this.animate){
13211             var _this = this;
13212             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13213         }else{
13214             this.touchViewEl.setStyle('display', 'none');
13215         }
13216         
13217     },
13218     
13219     setTouchViewValue : function()
13220     {
13221         if(this.multiple){
13222             this.clearItem();
13223         
13224             var _this = this;
13225
13226             Roo.each(this.tickItems, function(o){
13227                 this.addItem(o);
13228             }, this);
13229         }
13230         
13231         this.hideTouchView();
13232     },
13233     
13234     doTouchViewQuery : function()
13235     {
13236         Roo.log('doTouchViewQuery');
13237         
13238         var qe = {
13239             query: '',
13240             forceAll: true,
13241             combo: this,
13242             cancel:false
13243         };
13244         
13245         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13246             return false;
13247         }
13248         
13249         if(!this.alwaysQuery || this.mode == 'local'){
13250             this.onTouchViewLoad();
13251             return;
13252         }
13253         
13254         this.store.load();
13255     },
13256     
13257     onTouchViewBeforeLoad : function(combo,opts)
13258     {
13259         Roo.log('onTouchViewBeforeLoad');
13260         
13261         return;
13262     },
13263
13264     // private
13265     onTouchViewLoad : function()
13266     {
13267         Roo.log('onTouchViewLoad');
13268         
13269         if(this.store.getCount() < 1){
13270             this.onTouchViewEmptyResults();
13271             return;
13272         }
13273         
13274         this.clearTouchView();
13275         
13276         var rawValue = this.getRawValue();
13277         
13278         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13279         
13280         this.tickItems = [];
13281         
13282         this.store.data.each(function(d, rowIndex){
13283             var row = this.touchViewListGroup.createChild(template);
13284             
13285             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13286                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13287             }
13288             
13289             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13290                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13291             }
13292             
13293             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13294                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13295                 this.tickItems.push(d.data);
13296             }
13297             
13298             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13299             
13300         }, this);
13301         
13302         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13303         
13304         if(firstChecked){
13305             firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom);
13306         }
13307         
13308     },
13309     
13310     onTouchViewLoadException : function()
13311     {
13312         Roo.log('onTouchViewLoadException');
13313         
13314         this.hideTouchView();
13315     },
13316     
13317     onTouchViewEmptyResults : function()
13318     {
13319         Roo.log('onTouchViewEmptyResults');
13320         
13321         this.clearTouchView();
13322         
13323         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13324         
13325         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13326         
13327     },
13328     
13329     clearTouchView : function()
13330     {
13331         this.touchViewListGroup.dom.innerHTML = '';
13332     },
13333     
13334     onTouchViewClick : function(e, el, o)
13335     {
13336         e.preventDefault();
13337         
13338         var row = o.row;
13339         var rowIndex = o.rowIndex;
13340         
13341         var r = this.store.getAt(rowIndex);
13342         
13343         if(!this.multiple){
13344             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13345                 c.dom.removeAttribute('checked');
13346             }, this);
13347             
13348             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13349         
13350             this.setFromData(r.data);
13351             
13352             var close = this.closeTriggerEl();
13353         
13354             if(close){
13355                 close.show();
13356             }
13357
13358             this.hideTouchView();
13359             
13360             return;
13361         }
13362         
13363         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13364             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13365             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13366             return;
13367         }
13368         
13369         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13370         this.addItem(r.data);
13371         this.tickItems.push(r.data);
13372         
13373         
13374         
13375     }
13376     
13377
13378     /** 
13379     * @cfg {Boolean} grow 
13380     * @hide 
13381     */
13382     /** 
13383     * @cfg {Number} growMin 
13384     * @hide 
13385     */
13386     /** 
13387     * @cfg {Number} growMax 
13388     * @hide 
13389     */
13390     /**
13391      * @hide
13392      * @method autoSize
13393      */
13394 });
13395
13396 Roo.apply(Roo.bootstrap.ComboBox,  {
13397     
13398     header : {
13399         tag: 'div',
13400         cls: 'modal-header',
13401         cn: [
13402             {
13403                 tag: 'h4',
13404                 cls: 'modal-title'
13405             }
13406         ]
13407     },
13408     
13409     body : {
13410         tag: 'div',
13411         cls: 'modal-body',
13412         cn: [
13413             {
13414                 tag: 'ul',
13415                 cls: 'list-group'
13416             }
13417         ]
13418     },
13419     
13420     listItemRadio : {
13421         tag: 'li',
13422         cls: 'list-group-item',
13423         cn: [
13424             {
13425                 tag: 'span',
13426                 cls: 'roo-combobox-list-group-item-value'
13427             },
13428             {
13429                 tag: 'div',
13430                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13431                 cn: [
13432                     {
13433                         tag: 'input',
13434                         type: 'radio'
13435                     },
13436                     {
13437                         tag: 'label'
13438                     }
13439                 ]
13440             }
13441         ]
13442     },
13443     
13444     listItemCheckbox : {
13445         tag: 'li',
13446         cls: 'list-group-item',
13447         cn: [
13448             {
13449                 tag: 'span',
13450                 cls: 'roo-combobox-list-group-item-value'
13451             },
13452             {
13453                 tag: 'div',
13454                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13455                 cn: [
13456                     {
13457                         tag: 'input',
13458                         type: 'checkbox'
13459                     },
13460                     {
13461                         tag: 'label'
13462                     }
13463                 ]
13464             }
13465         ]
13466     },
13467     
13468     emptyResult : {
13469         tag: 'div',
13470         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13471     },
13472     
13473     footer : {
13474         tag: 'div',
13475         cls: 'modal-footer',
13476         cn: [
13477             {
13478                 tag: 'div',
13479                 cls: 'row',
13480                 cn: [
13481                     {
13482                         tag: 'div',
13483                         cls: 'col-xs-6 text-left',
13484                         cn: {
13485                             tag: 'button',
13486                             cls: 'btn btn-danger roo-touch-view-cancel',
13487                             html: 'Cancel'
13488                         }
13489                     },
13490                     {
13491                         tag: 'div',
13492                         cls: 'col-xs-6 text-right',
13493                         cn: {
13494                             tag: 'button',
13495                             cls: 'btn btn-success roo-touch-view-ok',
13496                             html: 'OK'
13497                         }
13498                     }
13499                 ]
13500             }
13501         ]
13502         
13503     }
13504 });
13505
13506 Roo.apply(Roo.bootstrap.ComboBox,  {
13507     
13508     touchViewTemplate : {
13509         tag: 'div',
13510         cls: 'modal fade roo-combobox-touch-view',
13511         cn: [
13512             {
13513                 tag: 'div',
13514                 cls: 'modal-dialog',
13515                 cn: [
13516                     {
13517                         tag: 'div',
13518                         cls: 'modal-content',
13519                         cn: [
13520                             Roo.bootstrap.ComboBox.header,
13521                             Roo.bootstrap.ComboBox.body,
13522                             Roo.bootstrap.ComboBox.footer
13523                         ]
13524                     }
13525                 ]
13526             }
13527         ]
13528     }
13529 });/*
13530  * Based on:
13531  * Ext JS Library 1.1.1
13532  * Copyright(c) 2006-2007, Ext JS, LLC.
13533  *
13534  * Originally Released Under LGPL - original licence link has changed is not relivant.
13535  *
13536  * Fork - LGPL
13537  * <script type="text/javascript">
13538  */
13539
13540 /**
13541  * @class Roo.View
13542  * @extends Roo.util.Observable
13543  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13544  * This class also supports single and multi selection modes. <br>
13545  * Create a data model bound view:
13546  <pre><code>
13547  var store = new Roo.data.Store(...);
13548
13549  var view = new Roo.View({
13550     el : "my-element",
13551     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13552  
13553     singleSelect: true,
13554     selectedClass: "ydataview-selected",
13555     store: store
13556  });
13557
13558  // listen for node click?
13559  view.on("click", function(vw, index, node, e){
13560  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13561  });
13562
13563  // load XML data
13564  dataModel.load("foobar.xml");
13565  </code></pre>
13566  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13567  * <br><br>
13568  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13569  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13570  * 
13571  * Note: old style constructor is still suported (container, template, config)
13572  * 
13573  * @constructor
13574  * Create a new View
13575  * @param {Object} config The config object
13576  * 
13577  */
13578 Roo.View = function(config, depreciated_tpl, depreciated_config){
13579     
13580     this.parent = false;
13581     
13582     if (typeof(depreciated_tpl) == 'undefined') {
13583         // new way.. - universal constructor.
13584         Roo.apply(this, config);
13585         this.el  = Roo.get(this.el);
13586     } else {
13587         // old format..
13588         this.el  = Roo.get(config);
13589         this.tpl = depreciated_tpl;
13590         Roo.apply(this, depreciated_config);
13591     }
13592     this.wrapEl  = this.el.wrap().wrap();
13593     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13594     
13595     
13596     if(typeof(this.tpl) == "string"){
13597         this.tpl = new Roo.Template(this.tpl);
13598     } else {
13599         // support xtype ctors..
13600         this.tpl = new Roo.factory(this.tpl, Roo);
13601     }
13602     
13603     
13604     this.tpl.compile();
13605     
13606     /** @private */
13607     this.addEvents({
13608         /**
13609          * @event beforeclick
13610          * Fires before a click is processed. Returns false to cancel the default action.
13611          * @param {Roo.View} this
13612          * @param {Number} index The index of the target node
13613          * @param {HTMLElement} node The target node
13614          * @param {Roo.EventObject} e The raw event object
13615          */
13616             "beforeclick" : true,
13617         /**
13618          * @event click
13619          * Fires when a template node is clicked.
13620          * @param {Roo.View} this
13621          * @param {Number} index The index of the target node
13622          * @param {HTMLElement} node The target node
13623          * @param {Roo.EventObject} e The raw event object
13624          */
13625             "click" : true,
13626         /**
13627          * @event dblclick
13628          * Fires when a template node is double clicked.
13629          * @param {Roo.View} this
13630          * @param {Number} index The index of the target node
13631          * @param {HTMLElement} node The target node
13632          * @param {Roo.EventObject} e The raw event object
13633          */
13634             "dblclick" : true,
13635         /**
13636          * @event contextmenu
13637          * Fires when a template node is right clicked.
13638          * @param {Roo.View} this
13639          * @param {Number} index The index of the target node
13640          * @param {HTMLElement} node The target node
13641          * @param {Roo.EventObject} e The raw event object
13642          */
13643             "contextmenu" : true,
13644         /**
13645          * @event selectionchange
13646          * Fires when the selected nodes change.
13647          * @param {Roo.View} this
13648          * @param {Array} selections Array of the selected nodes
13649          */
13650             "selectionchange" : true,
13651     
13652         /**
13653          * @event beforeselect
13654          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13655          * @param {Roo.View} this
13656          * @param {HTMLElement} node The node to be selected
13657          * @param {Array} selections Array of currently selected nodes
13658          */
13659             "beforeselect" : true,
13660         /**
13661          * @event preparedata
13662          * Fires on every row to render, to allow you to change the data.
13663          * @param {Roo.View} this
13664          * @param {Object} data to be rendered (change this)
13665          */
13666           "preparedata" : true
13667           
13668           
13669         });
13670
13671
13672
13673     this.el.on({
13674         "click": this.onClick,
13675         "dblclick": this.onDblClick,
13676         "contextmenu": this.onContextMenu,
13677         scope:this
13678     });
13679
13680     this.selections = [];
13681     this.nodes = [];
13682     this.cmp = new Roo.CompositeElementLite([]);
13683     if(this.store){
13684         this.store = Roo.factory(this.store, Roo.data);
13685         this.setStore(this.store, true);
13686     }
13687     
13688     if ( this.footer && this.footer.xtype) {
13689            
13690          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13691         
13692         this.footer.dataSource = this.store
13693         this.footer.container = fctr;
13694         this.footer = Roo.factory(this.footer, Roo);
13695         fctr.insertFirst(this.el);
13696         
13697         // this is a bit insane - as the paging toolbar seems to detach the el..
13698 //        dom.parentNode.parentNode.parentNode
13699          // they get detached?
13700     }
13701     
13702     
13703     Roo.View.superclass.constructor.call(this);
13704     
13705     
13706 };
13707
13708 Roo.extend(Roo.View, Roo.util.Observable, {
13709     
13710      /**
13711      * @cfg {Roo.data.Store} store Data store to load data from.
13712      */
13713     store : false,
13714     
13715     /**
13716      * @cfg {String|Roo.Element} el The container element.
13717      */
13718     el : '',
13719     
13720     /**
13721      * @cfg {String|Roo.Template} tpl The template used by this View 
13722      */
13723     tpl : false,
13724     /**
13725      * @cfg {String} dataName the named area of the template to use as the data area
13726      *                          Works with domtemplates roo-name="name"
13727      */
13728     dataName: false,
13729     /**
13730      * @cfg {String} selectedClass The css class to add to selected nodes
13731      */
13732     selectedClass : "x-view-selected",
13733      /**
13734      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13735      */
13736     emptyText : "",
13737     
13738     /**
13739      * @cfg {String} text to display on mask (default Loading)
13740      */
13741     mask : false,
13742     /**
13743      * @cfg {Boolean} multiSelect Allow multiple selection
13744      */
13745     multiSelect : false,
13746     /**
13747      * @cfg {Boolean} singleSelect Allow single selection
13748      */
13749     singleSelect:  false,
13750     
13751     /**
13752      * @cfg {Boolean} toggleSelect - selecting 
13753      */
13754     toggleSelect : false,
13755     
13756     /**
13757      * @cfg {Boolean} tickable - selecting 
13758      */
13759     tickable : false,
13760     
13761     /**
13762      * Returns the element this view is bound to.
13763      * @return {Roo.Element}
13764      */
13765     getEl : function(){
13766         return this.wrapEl;
13767     },
13768     
13769     
13770
13771     /**
13772      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13773      */
13774     refresh : function(){
13775         //Roo.log('refresh');
13776         var t = this.tpl;
13777         
13778         // if we are using something like 'domtemplate', then
13779         // the what gets used is:
13780         // t.applySubtemplate(NAME, data, wrapping data..)
13781         // the outer template then get' applied with
13782         //     the store 'extra data'
13783         // and the body get's added to the
13784         //      roo-name="data" node?
13785         //      <span class='roo-tpl-{name}'></span> ?????
13786         
13787         
13788         
13789         this.clearSelections();
13790         this.el.update("");
13791         var html = [];
13792         var records = this.store.getRange();
13793         if(records.length < 1) {
13794             
13795             // is this valid??  = should it render a template??
13796             
13797             this.el.update(this.emptyText);
13798             return;
13799         }
13800         var el = this.el;
13801         if (this.dataName) {
13802             this.el.update(t.apply(this.store.meta)); //????
13803             el = this.el.child('.roo-tpl-' + this.dataName);
13804         }
13805         
13806         for(var i = 0, len = records.length; i < len; i++){
13807             var data = this.prepareData(records[i].data, i, records[i]);
13808             this.fireEvent("preparedata", this, data, i, records[i]);
13809             
13810             var d = Roo.apply({}, data);
13811             
13812             if(this.tickable){
13813                 Roo.apply(d, {'roo-id' : Roo.id()});
13814                 
13815                 var _this = this;
13816             
13817                 Roo.each(this.parent.item, function(item){
13818                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13819                         return;
13820                     }
13821                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13822                 });
13823             }
13824             
13825             html[html.length] = Roo.util.Format.trim(
13826                 this.dataName ?
13827                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13828                     t.apply(d)
13829             );
13830         }
13831         
13832         
13833         
13834         el.update(html.join(""));
13835         this.nodes = el.dom.childNodes;
13836         this.updateIndexes(0);
13837     },
13838     
13839
13840     /**
13841      * Function to override to reformat the data that is sent to
13842      * the template for each node.
13843      * DEPRICATED - use the preparedata event handler.
13844      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13845      * a JSON object for an UpdateManager bound view).
13846      */
13847     prepareData : function(data, index, record)
13848     {
13849         this.fireEvent("preparedata", this, data, index, record);
13850         return data;
13851     },
13852
13853     onUpdate : function(ds, record){
13854         // Roo.log('on update');   
13855         this.clearSelections();
13856         var index = this.store.indexOf(record);
13857         var n = this.nodes[index];
13858         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13859         n.parentNode.removeChild(n);
13860         this.updateIndexes(index, index);
13861     },
13862
13863     
13864     
13865 // --------- FIXME     
13866     onAdd : function(ds, records, index)
13867     {
13868         //Roo.log(['on Add', ds, records, index] );        
13869         this.clearSelections();
13870         if(this.nodes.length == 0){
13871             this.refresh();
13872             return;
13873         }
13874         var n = this.nodes[index];
13875         for(var i = 0, len = records.length; i < len; i++){
13876             var d = this.prepareData(records[i].data, i, records[i]);
13877             if(n){
13878                 this.tpl.insertBefore(n, d);
13879             }else{
13880                 
13881                 this.tpl.append(this.el, d);
13882             }
13883         }
13884         this.updateIndexes(index);
13885     },
13886
13887     onRemove : function(ds, record, index){
13888        // Roo.log('onRemove');
13889         this.clearSelections();
13890         var el = this.dataName  ?
13891             this.el.child('.roo-tpl-' + this.dataName) :
13892             this.el; 
13893         
13894         el.dom.removeChild(this.nodes[index]);
13895         this.updateIndexes(index);
13896     },
13897
13898     /**
13899      * Refresh an individual node.
13900      * @param {Number} index
13901      */
13902     refreshNode : function(index){
13903         this.onUpdate(this.store, this.store.getAt(index));
13904     },
13905
13906     updateIndexes : function(startIndex, endIndex){
13907         var ns = this.nodes;
13908         startIndex = startIndex || 0;
13909         endIndex = endIndex || ns.length - 1;
13910         for(var i = startIndex; i <= endIndex; i++){
13911             ns[i].nodeIndex = i;
13912         }
13913     },
13914
13915     /**
13916      * Changes the data store this view uses and refresh the view.
13917      * @param {Store} store
13918      */
13919     setStore : function(store, initial){
13920         if(!initial && this.store){
13921             this.store.un("datachanged", this.refresh);
13922             this.store.un("add", this.onAdd);
13923             this.store.un("remove", this.onRemove);
13924             this.store.un("update", this.onUpdate);
13925             this.store.un("clear", this.refresh);
13926             this.store.un("beforeload", this.onBeforeLoad);
13927             this.store.un("load", this.onLoad);
13928             this.store.un("loadexception", this.onLoad);
13929         }
13930         if(store){
13931           
13932             store.on("datachanged", this.refresh, this);
13933             store.on("add", this.onAdd, this);
13934             store.on("remove", this.onRemove, this);
13935             store.on("update", this.onUpdate, this);
13936             store.on("clear", this.refresh, this);
13937             store.on("beforeload", this.onBeforeLoad, this);
13938             store.on("load", this.onLoad, this);
13939             store.on("loadexception", this.onLoad, this);
13940         }
13941         
13942         if(store){
13943             this.refresh();
13944         }
13945     },
13946     /**
13947      * onbeforeLoad - masks the loading area.
13948      *
13949      */
13950     onBeforeLoad : function(store,opts)
13951     {
13952          //Roo.log('onBeforeLoad');   
13953         if (!opts.add) {
13954             this.el.update("");
13955         }
13956         this.el.mask(this.mask ? this.mask : "Loading" ); 
13957     },
13958     onLoad : function ()
13959     {
13960         this.el.unmask();
13961     },
13962     
13963
13964     /**
13965      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13966      * @param {HTMLElement} node
13967      * @return {HTMLElement} The template node
13968      */
13969     findItemFromChild : function(node){
13970         var el = this.dataName  ?
13971             this.el.child('.roo-tpl-' + this.dataName,true) :
13972             this.el.dom; 
13973         
13974         if(!node || node.parentNode == el){
13975                     return node;
13976             }
13977             var p = node.parentNode;
13978             while(p && p != el){
13979             if(p.parentNode == el){
13980                 return p;
13981             }
13982             p = p.parentNode;
13983         }
13984             return null;
13985     },
13986
13987     /** @ignore */
13988     onClick : function(e){
13989         var item = this.findItemFromChild(e.getTarget());
13990         if(item){
13991             var index = this.indexOf(item);
13992             if(this.onItemClick(item, index, e) !== false){
13993                 this.fireEvent("click", this, index, item, e);
13994             }
13995         }else{
13996             this.clearSelections();
13997         }
13998     },
13999
14000     /** @ignore */
14001     onContextMenu : function(e){
14002         var item = this.findItemFromChild(e.getTarget());
14003         if(item){
14004             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14005         }
14006     },
14007
14008     /** @ignore */
14009     onDblClick : function(e){
14010         var item = this.findItemFromChild(e.getTarget());
14011         if(item){
14012             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14013         }
14014     },
14015
14016     onItemClick : function(item, index, e)
14017     {
14018         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14019             return false;
14020         }
14021         if (this.toggleSelect) {
14022             var m = this.isSelected(item) ? 'unselect' : 'select';
14023             //Roo.log(m);
14024             var _t = this;
14025             _t[m](item, true, false);
14026             return true;
14027         }
14028         if(this.multiSelect || this.singleSelect){
14029             if(this.multiSelect && e.shiftKey && this.lastSelection){
14030                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14031             }else{
14032                 this.select(item, this.multiSelect && e.ctrlKey);
14033                 this.lastSelection = item;
14034             }
14035             
14036             if(!this.tickable){
14037                 e.preventDefault();
14038             }
14039             
14040         }
14041         return true;
14042     },
14043
14044     /**
14045      * Get the number of selected nodes.
14046      * @return {Number}
14047      */
14048     getSelectionCount : function(){
14049         return this.selections.length;
14050     },
14051
14052     /**
14053      * Get the currently selected nodes.
14054      * @return {Array} An array of HTMLElements
14055      */
14056     getSelectedNodes : function(){
14057         return this.selections;
14058     },
14059
14060     /**
14061      * Get the indexes of the selected nodes.
14062      * @return {Array}
14063      */
14064     getSelectedIndexes : function(){
14065         var indexes = [], s = this.selections;
14066         for(var i = 0, len = s.length; i < len; i++){
14067             indexes.push(s[i].nodeIndex);
14068         }
14069         return indexes;
14070     },
14071
14072     /**
14073      * Clear all selections
14074      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14075      */
14076     clearSelections : function(suppressEvent){
14077         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14078             this.cmp.elements = this.selections;
14079             this.cmp.removeClass(this.selectedClass);
14080             this.selections = [];
14081             if(!suppressEvent){
14082                 this.fireEvent("selectionchange", this, this.selections);
14083             }
14084         }
14085     },
14086
14087     /**
14088      * Returns true if the passed node is selected
14089      * @param {HTMLElement/Number} node The node or node index
14090      * @return {Boolean}
14091      */
14092     isSelected : function(node){
14093         var s = this.selections;
14094         if(s.length < 1){
14095             return false;
14096         }
14097         node = this.getNode(node);
14098         return s.indexOf(node) !== -1;
14099     },
14100
14101     /**
14102      * Selects nodes.
14103      * @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
14104      * @param {Boolean} keepExisting (optional) true to keep existing selections
14105      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14106      */
14107     select : function(nodeInfo, keepExisting, suppressEvent){
14108         if(nodeInfo instanceof Array){
14109             if(!keepExisting){
14110                 this.clearSelections(true);
14111             }
14112             for(var i = 0, len = nodeInfo.length; i < len; i++){
14113                 this.select(nodeInfo[i], true, true);
14114             }
14115             return;
14116         } 
14117         var node = this.getNode(nodeInfo);
14118         if(!node || this.isSelected(node)){
14119             return; // already selected.
14120         }
14121         if(!keepExisting){
14122             this.clearSelections(true);
14123         }
14124         
14125         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14126             Roo.fly(node).addClass(this.selectedClass);
14127             this.selections.push(node);
14128             if(!suppressEvent){
14129                 this.fireEvent("selectionchange", this, this.selections);
14130             }
14131         }
14132         
14133         
14134     },
14135       /**
14136      * Unselects nodes.
14137      * @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
14138      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14139      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14140      */
14141     unselect : function(nodeInfo, keepExisting, suppressEvent)
14142     {
14143         if(nodeInfo instanceof Array){
14144             Roo.each(this.selections, function(s) {
14145                 this.unselect(s, nodeInfo);
14146             }, this);
14147             return;
14148         }
14149         var node = this.getNode(nodeInfo);
14150         if(!node || !this.isSelected(node)){
14151             //Roo.log("not selected");
14152             return; // not selected.
14153         }
14154         // fireevent???
14155         var ns = [];
14156         Roo.each(this.selections, function(s) {
14157             if (s == node ) {
14158                 Roo.fly(node).removeClass(this.selectedClass);
14159
14160                 return;
14161             }
14162             ns.push(s);
14163         },this);
14164         
14165         this.selections= ns;
14166         this.fireEvent("selectionchange", this, this.selections);
14167     },
14168
14169     /**
14170      * Gets a template node.
14171      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14172      * @return {HTMLElement} The node or null if it wasn't found
14173      */
14174     getNode : function(nodeInfo){
14175         if(typeof nodeInfo == "string"){
14176             return document.getElementById(nodeInfo);
14177         }else if(typeof nodeInfo == "number"){
14178             return this.nodes[nodeInfo];
14179         }
14180         return nodeInfo;
14181     },
14182
14183     /**
14184      * Gets a range template nodes.
14185      * @param {Number} startIndex
14186      * @param {Number} endIndex
14187      * @return {Array} An array of nodes
14188      */
14189     getNodes : function(start, end){
14190         var ns = this.nodes;
14191         start = start || 0;
14192         end = typeof end == "undefined" ? ns.length - 1 : end;
14193         var nodes = [];
14194         if(start <= end){
14195             for(var i = start; i <= end; i++){
14196                 nodes.push(ns[i]);
14197             }
14198         } else{
14199             for(var i = start; i >= end; i--){
14200                 nodes.push(ns[i]);
14201             }
14202         }
14203         return nodes;
14204     },
14205
14206     /**
14207      * Finds the index of the passed node
14208      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14209      * @return {Number} The index of the node or -1
14210      */
14211     indexOf : function(node){
14212         node = this.getNode(node);
14213         if(typeof node.nodeIndex == "number"){
14214             return node.nodeIndex;
14215         }
14216         var ns = this.nodes;
14217         for(var i = 0, len = ns.length; i < len; i++){
14218             if(ns[i] == node){
14219                 return i;
14220             }
14221         }
14222         return -1;
14223     }
14224 });
14225 /*
14226  * - LGPL
14227  *
14228  * based on jquery fullcalendar
14229  * 
14230  */
14231
14232 Roo.bootstrap = Roo.bootstrap || {};
14233 /**
14234  * @class Roo.bootstrap.Calendar
14235  * @extends Roo.bootstrap.Component
14236  * Bootstrap Calendar class
14237  * @cfg {Boolean} loadMask (true|false) default false
14238  * @cfg {Object} header generate the user specific header of the calendar, default false
14239
14240  * @constructor
14241  * Create a new Container
14242  * @param {Object} config The config object
14243  */
14244
14245
14246
14247 Roo.bootstrap.Calendar = function(config){
14248     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14249      this.addEvents({
14250         /**
14251              * @event select
14252              * Fires when a date is selected
14253              * @param {DatePicker} this
14254              * @param {Date} date The selected date
14255              */
14256         'select': true,
14257         /**
14258              * @event monthchange
14259              * Fires when the displayed month changes 
14260              * @param {DatePicker} this
14261              * @param {Date} date The selected month
14262              */
14263         'monthchange': true,
14264         /**
14265              * @event evententer
14266              * Fires when mouse over an event
14267              * @param {Calendar} this
14268              * @param {event} Event
14269              */
14270         'evententer': true,
14271         /**
14272              * @event eventleave
14273              * Fires when the mouse leaves an
14274              * @param {Calendar} this
14275              * @param {event}
14276              */
14277         'eventleave': true,
14278         /**
14279              * @event eventclick
14280              * Fires when the mouse click an
14281              * @param {Calendar} this
14282              * @param {event}
14283              */
14284         'eventclick': true
14285         
14286     });
14287
14288 };
14289
14290 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14291     
14292      /**
14293      * @cfg {Number} startDay
14294      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14295      */
14296     startDay : 0,
14297     
14298     loadMask : false,
14299     
14300     header : false,
14301       
14302     getAutoCreate : function(){
14303         
14304         
14305         var fc_button = function(name, corner, style, content ) {
14306             return Roo.apply({},{
14307                 tag : 'span',
14308                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14309                          (corner.length ?
14310                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14311                             ''
14312                         ),
14313                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14314                 unselectable: 'on'
14315             });
14316         };
14317         
14318         var header = {};
14319         
14320         if(!this.header){
14321             header = {
14322                 tag : 'table',
14323                 cls : 'fc-header',
14324                 style : 'width:100%',
14325                 cn : [
14326                     {
14327                         tag: 'tr',
14328                         cn : [
14329                             {
14330                                 tag : 'td',
14331                                 cls : 'fc-header-left',
14332                                 cn : [
14333                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14334                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14335                                     { tag: 'span', cls: 'fc-header-space' },
14336                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14337
14338
14339                                 ]
14340                             },
14341
14342                             {
14343                                 tag : 'td',
14344                                 cls : 'fc-header-center',
14345                                 cn : [
14346                                     {
14347                                         tag: 'span',
14348                                         cls: 'fc-header-title',
14349                                         cn : {
14350                                             tag: 'H2',
14351                                             html : 'month / year'
14352                                         }
14353                                     }
14354
14355                                 ]
14356                             },
14357                             {
14358                                 tag : 'td',
14359                                 cls : 'fc-header-right',
14360                                 cn : [
14361                               /*      fc_button('month', 'left', '', 'month' ),
14362                                     fc_button('week', '', '', 'week' ),
14363                                     fc_button('day', 'right', '', 'day' )
14364                                 */    
14365
14366                                 ]
14367                             }
14368
14369                         ]
14370                     }
14371                 ]
14372             };
14373         }
14374         
14375         header = this.header;
14376         
14377        
14378         var cal_heads = function() {
14379             var ret = [];
14380             // fixme - handle this.
14381             
14382             for (var i =0; i < Date.dayNames.length; i++) {
14383                 var d = Date.dayNames[i];
14384                 ret.push({
14385                     tag: 'th',
14386                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14387                     html : d.substring(0,3)
14388                 });
14389                 
14390             }
14391             ret[0].cls += ' fc-first';
14392             ret[6].cls += ' fc-last';
14393             return ret;
14394         };
14395         var cal_cell = function(n) {
14396             return  {
14397                 tag: 'td',
14398                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14399                 cn : [
14400                     {
14401                         cn : [
14402                             {
14403                                 cls: 'fc-day-number',
14404                                 html: 'D'
14405                             },
14406                             {
14407                                 cls: 'fc-day-content',
14408                              
14409                                 cn : [
14410                                      {
14411                                         style: 'position: relative;' // height: 17px;
14412                                     }
14413                                 ]
14414                             }
14415                             
14416                             
14417                         ]
14418                     }
14419                 ]
14420                 
14421             }
14422         };
14423         var cal_rows = function() {
14424             
14425             var ret = [];
14426             for (var r = 0; r < 6; r++) {
14427                 var row= {
14428                     tag : 'tr',
14429                     cls : 'fc-week',
14430                     cn : []
14431                 };
14432                 
14433                 for (var i =0; i < Date.dayNames.length; i++) {
14434                     var d = Date.dayNames[i];
14435                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14436
14437                 }
14438                 row.cn[0].cls+=' fc-first';
14439                 row.cn[0].cn[0].style = 'min-height:90px';
14440                 row.cn[6].cls+=' fc-last';
14441                 ret.push(row);
14442                 
14443             }
14444             ret[0].cls += ' fc-first';
14445             ret[4].cls += ' fc-prev-last';
14446             ret[5].cls += ' fc-last';
14447             return ret;
14448             
14449         };
14450         
14451         var cal_table = {
14452             tag: 'table',
14453             cls: 'fc-border-separate',
14454             style : 'width:100%',
14455             cellspacing  : 0,
14456             cn : [
14457                 { 
14458                     tag: 'thead',
14459                     cn : [
14460                         { 
14461                             tag: 'tr',
14462                             cls : 'fc-first fc-last',
14463                             cn : cal_heads()
14464                         }
14465                     ]
14466                 },
14467                 { 
14468                     tag: 'tbody',
14469                     cn : cal_rows()
14470                 }
14471                   
14472             ]
14473         };
14474          
14475          var cfg = {
14476             cls : 'fc fc-ltr',
14477             cn : [
14478                 header,
14479                 {
14480                     cls : 'fc-content',
14481                     style : "position: relative;",
14482                     cn : [
14483                         {
14484                             cls : 'fc-view fc-view-month fc-grid',
14485                             style : 'position: relative',
14486                             unselectable : 'on',
14487                             cn : [
14488                                 {
14489                                     cls : 'fc-event-container',
14490                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14491                                 },
14492                                 cal_table
14493                             ]
14494                         }
14495                     ]
14496     
14497                 }
14498            ] 
14499             
14500         };
14501         
14502          
14503         
14504         return cfg;
14505     },
14506     
14507     
14508     initEvents : function()
14509     {
14510         if(!this.store){
14511             throw "can not find store for calendar";
14512         }
14513         
14514         var mark = {
14515             tag: "div",
14516             cls:"x-dlg-mask",
14517             style: "text-align:center",
14518             cn: [
14519                 {
14520                     tag: "div",
14521                     style: "background-color:white;width:50%;margin:250 auto",
14522                     cn: [
14523                         {
14524                             tag: "img",
14525                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14526                         },
14527                         {
14528                             tag: "span",
14529                             html: "Loading"
14530                         }
14531                         
14532                     ]
14533                 }
14534             ]
14535         }
14536         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14537         
14538         var size = this.el.select('.fc-content', true).first().getSize();
14539         this.maskEl.setSize(size.width, size.height);
14540         this.maskEl.enableDisplayMode("block");
14541         if(!this.loadMask){
14542             this.maskEl.hide();
14543         }
14544         
14545         this.store = Roo.factory(this.store, Roo.data);
14546         this.store.on('load', this.onLoad, this);
14547         this.store.on('beforeload', this.onBeforeLoad, this);
14548         
14549         this.resize();
14550         
14551         this.cells = this.el.select('.fc-day',true);
14552         //Roo.log(this.cells);
14553         this.textNodes = this.el.query('.fc-day-number');
14554         this.cells.addClassOnOver('fc-state-hover');
14555         
14556         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14557         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14558         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14559         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14560         
14561         this.on('monthchange', this.onMonthChange, this);
14562         
14563         this.update(new Date().clearTime());
14564     },
14565     
14566     resize : function() {
14567         var sz  = this.el.getSize();
14568         
14569         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14570         this.el.select('.fc-day-content div',true).setHeight(34);
14571     },
14572     
14573     
14574     // private
14575     showPrevMonth : function(e){
14576         this.update(this.activeDate.add("mo", -1));
14577     },
14578     showToday : function(e){
14579         this.update(new Date().clearTime());
14580     },
14581     // private
14582     showNextMonth : function(e){
14583         this.update(this.activeDate.add("mo", 1));
14584     },
14585
14586     // private
14587     showPrevYear : function(){
14588         this.update(this.activeDate.add("y", -1));
14589     },
14590
14591     // private
14592     showNextYear : function(){
14593         this.update(this.activeDate.add("y", 1));
14594     },
14595
14596     
14597    // private
14598     update : function(date)
14599     {
14600         var vd = this.activeDate;
14601         this.activeDate = date;
14602 //        if(vd && this.el){
14603 //            var t = date.getTime();
14604 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14605 //                Roo.log('using add remove');
14606 //                
14607 //                this.fireEvent('monthchange', this, date);
14608 //                
14609 //                this.cells.removeClass("fc-state-highlight");
14610 //                this.cells.each(function(c){
14611 //                   if(c.dateValue == t){
14612 //                       c.addClass("fc-state-highlight");
14613 //                       setTimeout(function(){
14614 //                            try{c.dom.firstChild.focus();}catch(e){}
14615 //                       }, 50);
14616 //                       return false;
14617 //                   }
14618 //                   return true;
14619 //                });
14620 //                return;
14621 //            }
14622 //        }
14623         
14624         var days = date.getDaysInMonth();
14625         
14626         var firstOfMonth = date.getFirstDateOfMonth();
14627         var startingPos = firstOfMonth.getDay()-this.startDay;
14628         
14629         if(startingPos < this.startDay){
14630             startingPos += 7;
14631         }
14632         
14633         var pm = date.add(Date.MONTH, -1);
14634         var prevStart = pm.getDaysInMonth()-startingPos;
14635 //        
14636         this.cells = this.el.select('.fc-day',true);
14637         this.textNodes = this.el.query('.fc-day-number');
14638         this.cells.addClassOnOver('fc-state-hover');
14639         
14640         var cells = this.cells.elements;
14641         var textEls = this.textNodes;
14642         
14643         Roo.each(cells, function(cell){
14644             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14645         });
14646         
14647         days += startingPos;
14648
14649         // convert everything to numbers so it's fast
14650         var day = 86400000;
14651         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14652         //Roo.log(d);
14653         //Roo.log(pm);
14654         //Roo.log(prevStart);
14655         
14656         var today = new Date().clearTime().getTime();
14657         var sel = date.clearTime().getTime();
14658         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14659         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14660         var ddMatch = this.disabledDatesRE;
14661         var ddText = this.disabledDatesText;
14662         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14663         var ddaysText = this.disabledDaysText;
14664         var format = this.format;
14665         
14666         var setCellClass = function(cal, cell){
14667             cell.row = 0;
14668             cell.events = [];
14669             cell.more = [];
14670             //Roo.log('set Cell Class');
14671             cell.title = "";
14672             var t = d.getTime();
14673             
14674             //Roo.log(d);
14675             
14676             cell.dateValue = t;
14677             if(t == today){
14678                 cell.className += " fc-today";
14679                 cell.className += " fc-state-highlight";
14680                 cell.title = cal.todayText;
14681             }
14682             if(t == sel){
14683                 // disable highlight in other month..
14684                 //cell.className += " fc-state-highlight";
14685                 
14686             }
14687             // disabling
14688             if(t < min) {
14689                 cell.className = " fc-state-disabled";
14690                 cell.title = cal.minText;
14691                 return;
14692             }
14693             if(t > max) {
14694                 cell.className = " fc-state-disabled";
14695                 cell.title = cal.maxText;
14696                 return;
14697             }
14698             if(ddays){
14699                 if(ddays.indexOf(d.getDay()) != -1){
14700                     cell.title = ddaysText;
14701                     cell.className = " fc-state-disabled";
14702                 }
14703             }
14704             if(ddMatch && format){
14705                 var fvalue = d.dateFormat(format);
14706                 if(ddMatch.test(fvalue)){
14707                     cell.title = ddText.replace("%0", fvalue);
14708                     cell.className = " fc-state-disabled";
14709                 }
14710             }
14711             
14712             if (!cell.initialClassName) {
14713                 cell.initialClassName = cell.dom.className;
14714             }
14715             
14716             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14717         };
14718
14719         var i = 0;
14720         
14721         for(; i < startingPos; i++) {
14722             textEls[i].innerHTML = (++prevStart);
14723             d.setDate(d.getDate()+1);
14724             
14725             cells[i].className = "fc-past fc-other-month";
14726             setCellClass(this, cells[i]);
14727         }
14728         
14729         var intDay = 0;
14730         
14731         for(; i < days; i++){
14732             intDay = i - startingPos + 1;
14733             textEls[i].innerHTML = (intDay);
14734             d.setDate(d.getDate()+1);
14735             
14736             cells[i].className = ''; // "x-date-active";
14737             setCellClass(this, cells[i]);
14738         }
14739         var extraDays = 0;
14740         
14741         for(; i < 42; i++) {
14742             textEls[i].innerHTML = (++extraDays);
14743             d.setDate(d.getDate()+1);
14744             
14745             cells[i].className = "fc-future fc-other-month";
14746             setCellClass(this, cells[i]);
14747         }
14748         
14749         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14750         
14751         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14752         
14753         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14754         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14755         
14756         if(totalRows != 6){
14757             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14758             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14759         }
14760         
14761         this.fireEvent('monthchange', this, date);
14762         
14763         
14764         /*
14765         if(!this.internalRender){
14766             var main = this.el.dom.firstChild;
14767             var w = main.offsetWidth;
14768             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14769             Roo.fly(main).setWidth(w);
14770             this.internalRender = true;
14771             // opera does not respect the auto grow header center column
14772             // then, after it gets a width opera refuses to recalculate
14773             // without a second pass
14774             if(Roo.isOpera && !this.secondPass){
14775                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14776                 this.secondPass = true;
14777                 this.update.defer(10, this, [date]);
14778             }
14779         }
14780         */
14781         
14782     },
14783     
14784     findCell : function(dt) {
14785         dt = dt.clearTime().getTime();
14786         var ret = false;
14787         this.cells.each(function(c){
14788             //Roo.log("check " +c.dateValue + '?=' + dt);
14789             if(c.dateValue == dt){
14790                 ret = c;
14791                 return false;
14792             }
14793             return true;
14794         });
14795         
14796         return ret;
14797     },
14798     
14799     findCells : function(ev) {
14800         var s = ev.start.clone().clearTime().getTime();
14801        // Roo.log(s);
14802         var e= ev.end.clone().clearTime().getTime();
14803        // Roo.log(e);
14804         var ret = [];
14805         this.cells.each(function(c){
14806              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14807             
14808             if(c.dateValue > e){
14809                 return ;
14810             }
14811             if(c.dateValue < s){
14812                 return ;
14813             }
14814             ret.push(c);
14815         });
14816         
14817         return ret;    
14818     },
14819     
14820 //    findBestRow: function(cells)
14821 //    {
14822 //        var ret = 0;
14823 //        
14824 //        for (var i =0 ; i < cells.length;i++) {
14825 //            ret  = Math.max(cells[i].rows || 0,ret);
14826 //        }
14827 //        return ret;
14828 //        
14829 //    },
14830     
14831     
14832     addItem : function(ev)
14833     {
14834         // look for vertical location slot in
14835         var cells = this.findCells(ev);
14836         
14837 //        ev.row = this.findBestRow(cells);
14838         
14839         // work out the location.
14840         
14841         var crow = false;
14842         var rows = [];
14843         for(var i =0; i < cells.length; i++) {
14844             
14845             cells[i].row = cells[0].row;
14846             
14847             if(i == 0){
14848                 cells[i].row = cells[i].row + 1;
14849             }
14850             
14851             if (!crow) {
14852                 crow = {
14853                     start : cells[i],
14854                     end :  cells[i]
14855                 };
14856                 continue;
14857             }
14858             if (crow.start.getY() == cells[i].getY()) {
14859                 // on same row.
14860                 crow.end = cells[i];
14861                 continue;
14862             }
14863             // different row.
14864             rows.push(crow);
14865             crow = {
14866                 start: cells[i],
14867                 end : cells[i]
14868             };
14869             
14870         }
14871         
14872         rows.push(crow);
14873         ev.els = [];
14874         ev.rows = rows;
14875         ev.cells = cells;
14876         
14877         cells[0].events.push(ev);
14878         
14879         this.calevents.push(ev);
14880     },
14881     
14882     clearEvents: function() {
14883         
14884         if(!this.calevents){
14885             return;
14886         }
14887         
14888         Roo.each(this.cells.elements, function(c){
14889             c.row = 0;
14890             c.events = [];
14891             c.more = [];
14892         });
14893         
14894         Roo.each(this.calevents, function(e) {
14895             Roo.each(e.els, function(el) {
14896                 el.un('mouseenter' ,this.onEventEnter, this);
14897                 el.un('mouseleave' ,this.onEventLeave, this);
14898                 el.remove();
14899             },this);
14900         },this);
14901         
14902         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14903             e.remove();
14904         });
14905         
14906     },
14907     
14908     renderEvents: function()
14909     {   
14910         var _this = this;
14911         
14912         this.cells.each(function(c) {
14913             
14914             if(c.row < 5){
14915                 return;
14916             }
14917             
14918             var ev = c.events;
14919             
14920             var r = 4;
14921             if(c.row != c.events.length){
14922                 r = 4 - (4 - (c.row - c.events.length));
14923             }
14924             
14925             c.events = ev.slice(0, r);
14926             c.more = ev.slice(r);
14927             
14928             if(c.more.length && c.more.length == 1){
14929                 c.events.push(c.more.pop());
14930             }
14931             
14932             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14933             
14934         });
14935             
14936         this.cells.each(function(c) {
14937             
14938             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14939             
14940             
14941             for (var e = 0; e < c.events.length; e++){
14942                 var ev = c.events[e];
14943                 var rows = ev.rows;
14944                 
14945                 for(var i = 0; i < rows.length; i++) {
14946                 
14947                     // how many rows should it span..
14948
14949                     var  cfg = {
14950                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14951                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14952
14953                         unselectable : "on",
14954                         cn : [
14955                             {
14956                                 cls: 'fc-event-inner',
14957                                 cn : [
14958     //                                {
14959     //                                  tag:'span',
14960     //                                  cls: 'fc-event-time',
14961     //                                  html : cells.length > 1 ? '' : ev.time
14962     //                                },
14963                                     {
14964                                       tag:'span',
14965                                       cls: 'fc-event-title',
14966                                       html : String.format('{0}', ev.title)
14967                                     }
14968
14969
14970                                 ]
14971                             },
14972                             {
14973                                 cls: 'ui-resizable-handle ui-resizable-e',
14974                                 html : '&nbsp;&nbsp;&nbsp'
14975                             }
14976
14977                         ]
14978                     };
14979
14980                     if (i == 0) {
14981                         cfg.cls += ' fc-event-start';
14982                     }
14983                     if ((i+1) == rows.length) {
14984                         cfg.cls += ' fc-event-end';
14985                     }
14986
14987                     var ctr = _this.el.select('.fc-event-container',true).first();
14988                     var cg = ctr.createChild(cfg);
14989
14990                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14991                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14992
14993                     var r = (c.more.length) ? 1 : 0;
14994                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14995                     cg.setWidth(ebox.right - sbox.x -2);
14996
14997                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14998                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14999                     cg.on('click', _this.onEventClick, _this, ev);
15000
15001                     ev.els.push(cg);
15002                     
15003                 }
15004                 
15005             }
15006             
15007             
15008             if(c.more.length){
15009                 var  cfg = {
15010                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15011                     style : 'position: absolute',
15012                     unselectable : "on",
15013                     cn : [
15014                         {
15015                             cls: 'fc-event-inner',
15016                             cn : [
15017                                 {
15018                                   tag:'span',
15019                                   cls: 'fc-event-title',
15020                                   html : 'More'
15021                                 }
15022
15023
15024                             ]
15025                         },
15026                         {
15027                             cls: 'ui-resizable-handle ui-resizable-e',
15028                             html : '&nbsp;&nbsp;&nbsp'
15029                         }
15030
15031                     ]
15032                 };
15033
15034                 var ctr = _this.el.select('.fc-event-container',true).first();
15035                 var cg = ctr.createChild(cfg);
15036
15037                 var sbox = c.select('.fc-day-content',true).first().getBox();
15038                 var ebox = c.select('.fc-day-content',true).first().getBox();
15039                 //Roo.log(cg);
15040                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15041                 cg.setWidth(ebox.right - sbox.x -2);
15042
15043                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15044                 
15045             }
15046             
15047         });
15048         
15049         
15050         
15051     },
15052     
15053     onEventEnter: function (e, el,event,d) {
15054         this.fireEvent('evententer', this, el, event);
15055     },
15056     
15057     onEventLeave: function (e, el,event,d) {
15058         this.fireEvent('eventleave', this, el, event);
15059     },
15060     
15061     onEventClick: function (e, el,event,d) {
15062         this.fireEvent('eventclick', this, el, event);
15063     },
15064     
15065     onMonthChange: function () {
15066         this.store.load();
15067     },
15068     
15069     onMoreEventClick: function(e, el, more)
15070     {
15071         var _this = this;
15072         
15073         this.calpopover.placement = 'right';
15074         this.calpopover.setTitle('More');
15075         
15076         this.calpopover.setContent('');
15077         
15078         var ctr = this.calpopover.el.select('.popover-content', true).first();
15079         
15080         Roo.each(more, function(m){
15081             var cfg = {
15082                 cls : 'fc-event-hori fc-event-draggable',
15083                 html : m.title
15084             }
15085             var cg = ctr.createChild(cfg);
15086             
15087             cg.on('click', _this.onEventClick, _this, m);
15088         });
15089         
15090         this.calpopover.show(el);
15091         
15092         
15093     },
15094     
15095     onLoad: function () 
15096     {   
15097         this.calevents = [];
15098         var cal = this;
15099         
15100         if(this.store.getCount() > 0){
15101             this.store.data.each(function(d){
15102                cal.addItem({
15103                     id : d.data.id,
15104                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15105                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15106                     time : d.data.start_time,
15107                     title : d.data.title,
15108                     description : d.data.description,
15109                     venue : d.data.venue
15110                 });
15111             });
15112         }
15113         
15114         this.renderEvents();
15115         
15116         if(this.calevents.length && this.loadMask){
15117             this.maskEl.hide();
15118         }
15119     },
15120     
15121     onBeforeLoad: function()
15122     {
15123         this.clearEvents();
15124         if(this.loadMask){
15125             this.maskEl.show();
15126         }
15127     }
15128 });
15129
15130  
15131  /*
15132  * - LGPL
15133  *
15134  * element
15135  * 
15136  */
15137
15138 /**
15139  * @class Roo.bootstrap.Popover
15140  * @extends Roo.bootstrap.Component
15141  * Bootstrap Popover class
15142  * @cfg {String} html contents of the popover   (or false to use children..)
15143  * @cfg {String} title of popover (or false to hide)
15144  * @cfg {String} placement how it is placed
15145  * @cfg {String} trigger click || hover (or false to trigger manually)
15146  * @cfg {String} over what (parent or false to trigger manually.)
15147  * @cfg {Number} delay - delay before showing
15148  
15149  * @constructor
15150  * Create a new Popover
15151  * @param {Object} config The config object
15152  */
15153
15154 Roo.bootstrap.Popover = function(config){
15155     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15156 };
15157
15158 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15159     
15160     title: 'Fill in a title',
15161     html: false,
15162     
15163     placement : 'right',
15164     trigger : 'hover', // hover
15165     
15166     delay : 0,
15167     
15168     over: 'parent',
15169     
15170     can_build_overlaid : false,
15171     
15172     getChildContainer : function()
15173     {
15174         return this.el.select('.popover-content',true).first();
15175     },
15176     
15177     getAutoCreate : function(){
15178          Roo.log('make popover?');
15179         var cfg = {
15180            cls : 'popover roo-dynamic',
15181            style: 'display:block',
15182            cn : [
15183                 {
15184                     cls : 'arrow'
15185                 },
15186                 {
15187                     cls : 'popover-inner',
15188                     cn : [
15189                         {
15190                             tag: 'h3',
15191                             cls: 'popover-title',
15192                             html : this.title
15193                         },
15194                         {
15195                             cls : 'popover-content',
15196                             html : this.html
15197                         }
15198                     ]
15199                     
15200                 }
15201            ]
15202         };
15203         
15204         return cfg;
15205     },
15206     setTitle: function(str)
15207     {
15208         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15209     },
15210     setContent: function(str)
15211     {
15212         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15213     },
15214     // as it get's added to the bottom of the page.
15215     onRender : function(ct, position)
15216     {
15217         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15218         if(!this.el){
15219             var cfg = Roo.apply({},  this.getAutoCreate());
15220             cfg.id = Roo.id();
15221             
15222             if (this.cls) {
15223                 cfg.cls += ' ' + this.cls;
15224             }
15225             if (this.style) {
15226                 cfg.style = this.style;
15227             }
15228             Roo.log("adding to ")
15229             this.el = Roo.get(document.body).createChild(cfg, position);
15230             Roo.log(this.el);
15231         }
15232         this.initEvents();
15233     },
15234     
15235     initEvents : function()
15236     {
15237         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15238         this.el.enableDisplayMode('block');
15239         this.el.hide();
15240         if (this.over === false) {
15241             return; 
15242         }
15243         if (this.triggers === false) {
15244             return;
15245         }
15246         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15247         var triggers = this.trigger ? this.trigger.split(' ') : [];
15248         Roo.each(triggers, function(trigger) {
15249         
15250             if (trigger == 'click') {
15251                 on_el.on('click', this.toggle, this);
15252             } else if (trigger != 'manual') {
15253                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15254                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15255       
15256                 on_el.on(eventIn  ,this.enter, this);
15257                 on_el.on(eventOut, this.leave, this);
15258             }
15259         }, this);
15260         
15261     },
15262     
15263     
15264     // private
15265     timeout : null,
15266     hoverState : null,
15267     
15268     toggle : function () {
15269         this.hoverState == 'in' ? this.leave() : this.enter();
15270     },
15271     
15272     enter : function () {
15273        
15274     
15275         clearTimeout(this.timeout);
15276     
15277         this.hoverState = 'in';
15278     
15279         if (!this.delay || !this.delay.show) {
15280             this.show();
15281             return;
15282         }
15283         var _t = this;
15284         this.timeout = setTimeout(function () {
15285             if (_t.hoverState == 'in') {
15286                 _t.show();
15287             }
15288         }, this.delay.show)
15289     },
15290     leave : function() {
15291         clearTimeout(this.timeout);
15292     
15293         this.hoverState = 'out';
15294     
15295         if (!this.delay || !this.delay.hide) {
15296             this.hide();
15297             return;
15298         }
15299         var _t = this;
15300         this.timeout = setTimeout(function () {
15301             if (_t.hoverState == 'out') {
15302                 _t.hide();
15303             }
15304         }, this.delay.hide)
15305     },
15306     
15307     show : function (on_el)
15308     {
15309         if (!on_el) {
15310             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15311         }
15312         // set content.
15313         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15314         if (this.html !== false) {
15315             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
15316         }
15317         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15318         if (!this.title.length) {
15319             this.el.select('.popover-title',true).hide();
15320         }
15321         
15322         var placement = typeof this.placement == 'function' ?
15323             this.placement.call(this, this.el, on_el) :
15324             this.placement;
15325             
15326         var autoToken = /\s?auto?\s?/i;
15327         var autoPlace = autoToken.test(placement);
15328         if (autoPlace) {
15329             placement = placement.replace(autoToken, '') || 'top';
15330         }
15331         
15332         //this.el.detach()
15333         //this.el.setXY([0,0]);
15334         this.el.show();
15335         this.el.dom.style.display='block';
15336         this.el.addClass(placement);
15337         
15338         //this.el.appendTo(on_el);
15339         
15340         var p = this.getPosition();
15341         var box = this.el.getBox();
15342         
15343         if (autoPlace) {
15344             // fixme..
15345         }
15346         var align = Roo.bootstrap.Popover.alignment[placement];
15347         this.el.alignTo(on_el, align[0],align[1]);
15348         //var arrow = this.el.select('.arrow',true).first();
15349         //arrow.set(align[2], 
15350         
15351         this.el.addClass('in');
15352         this.hoverState = null;
15353         
15354         if (this.el.hasClass('fade')) {
15355             // fade it?
15356         }
15357         
15358     },
15359     hide : function()
15360     {
15361         this.el.setXY([0,0]);
15362         this.el.removeClass('in');
15363         this.el.hide();
15364         
15365     }
15366     
15367 });
15368
15369 Roo.bootstrap.Popover.alignment = {
15370     'left' : ['r-l', [-10,0], 'right'],
15371     'right' : ['l-r', [10,0], 'left'],
15372     'bottom' : ['t-b', [0,10], 'top'],
15373     'top' : [ 'b-t', [0,-10], 'bottom']
15374 };
15375
15376  /*
15377  * - LGPL
15378  *
15379  * Progress
15380  * 
15381  */
15382
15383 /**
15384  * @class Roo.bootstrap.Progress
15385  * @extends Roo.bootstrap.Component
15386  * Bootstrap Progress class
15387  * @cfg {Boolean} striped striped of the progress bar
15388  * @cfg {Boolean} active animated of the progress bar
15389  * 
15390  * 
15391  * @constructor
15392  * Create a new Progress
15393  * @param {Object} config The config object
15394  */
15395
15396 Roo.bootstrap.Progress = function(config){
15397     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15398 };
15399
15400 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15401     
15402     striped : false,
15403     active: false,
15404     
15405     getAutoCreate : function(){
15406         var cfg = {
15407             tag: 'div',
15408             cls: 'progress'
15409         };
15410         
15411         
15412         if(this.striped){
15413             cfg.cls += ' progress-striped';
15414         }
15415       
15416         if(this.active){
15417             cfg.cls += ' active';
15418         }
15419         
15420         
15421         return cfg;
15422     }
15423    
15424 });
15425
15426  
15427
15428  /*
15429  * - LGPL
15430  *
15431  * ProgressBar
15432  * 
15433  */
15434
15435 /**
15436  * @class Roo.bootstrap.ProgressBar
15437  * @extends Roo.bootstrap.Component
15438  * Bootstrap ProgressBar class
15439  * @cfg {Number} aria_valuenow aria-value now
15440  * @cfg {Number} aria_valuemin aria-value min
15441  * @cfg {Number} aria_valuemax aria-value max
15442  * @cfg {String} label label for the progress bar
15443  * @cfg {String} panel (success | info | warning | danger )
15444  * @cfg {String} role role of the progress bar
15445  * @cfg {String} sr_only text
15446  * 
15447  * 
15448  * @constructor
15449  * Create a new ProgressBar
15450  * @param {Object} config The config object
15451  */
15452
15453 Roo.bootstrap.ProgressBar = function(config){
15454     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15455 };
15456
15457 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15458     
15459     aria_valuenow : 0,
15460     aria_valuemin : 0,
15461     aria_valuemax : 100,
15462     label : false,
15463     panel : false,
15464     role : false,
15465     sr_only: false,
15466     
15467     getAutoCreate : function()
15468     {
15469         
15470         var cfg = {
15471             tag: 'div',
15472             cls: 'progress-bar',
15473             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15474         };
15475         
15476         if(this.sr_only){
15477             cfg.cn = {
15478                 tag: 'span',
15479                 cls: 'sr-only',
15480                 html: this.sr_only
15481             }
15482         }
15483         
15484         if(this.role){
15485             cfg.role = this.role;
15486         }
15487         
15488         if(this.aria_valuenow){
15489             cfg['aria-valuenow'] = this.aria_valuenow;
15490         }
15491         
15492         if(this.aria_valuemin){
15493             cfg['aria-valuemin'] = this.aria_valuemin;
15494         }
15495         
15496         if(this.aria_valuemax){
15497             cfg['aria-valuemax'] = this.aria_valuemax;
15498         }
15499         
15500         if(this.label && !this.sr_only){
15501             cfg.html = this.label;
15502         }
15503         
15504         if(this.panel){
15505             cfg.cls += ' progress-bar-' + this.panel;
15506         }
15507         
15508         return cfg;
15509     },
15510     
15511     update : function(aria_valuenow)
15512     {
15513         this.aria_valuenow = aria_valuenow;
15514         
15515         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15516     }
15517    
15518 });
15519
15520  
15521
15522  /*
15523  * - LGPL
15524  *
15525  * column
15526  * 
15527  */
15528
15529 /**
15530  * @class Roo.bootstrap.TabGroup
15531  * @extends Roo.bootstrap.Column
15532  * Bootstrap Column class
15533  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15534  * @cfg {Boolean} carousel true to make the group behave like a carousel
15535  * @cfg {Number} bullets show the panel pointer.. default 0
15536  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15537  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15538  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15539  * 
15540  * @constructor
15541  * Create a new TabGroup
15542  * @param {Object} config The config object
15543  */
15544
15545 Roo.bootstrap.TabGroup = function(config){
15546     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15547     if (!this.navId) {
15548         this.navId = Roo.id();
15549     }
15550     this.tabs = [];
15551     Roo.bootstrap.TabGroup.register(this);
15552     
15553 };
15554
15555 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15556     
15557     carousel : false,
15558     transition : false,
15559     bullets : 0,
15560     timer : 0,
15561     autoslide : false,
15562     slideFn : false,
15563     slideOnTouch : false,
15564     
15565     getAutoCreate : function()
15566     {
15567         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15568         
15569         cfg.cls += ' tab-content';
15570         
15571         Roo.log('get auto create...............');
15572         
15573         if (this.carousel) {
15574             cfg.cls += ' carousel slide';
15575             
15576             cfg.cn = [{
15577                cls : 'carousel-inner'
15578             }];
15579         
15580             if(this.bullets > 0 && !Roo.isTouch){
15581                 
15582                 var bullets = {
15583                     cls : 'carousel-bullets',
15584                     cn : []
15585                 };
15586                 
15587                 if(this.bullets_cls){
15588                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15589                 }
15590                 
15591                 for (var i = 0; i < this.bullets; i++){
15592                     bullets.cn.push({
15593                         cls : 'bullet bullet-' + i
15594                     });
15595                 }
15596                 
15597                 bullets.cn.push({
15598                     cls : 'clear'
15599                 });
15600                 
15601                 cfg.cn[0].cn = bullets;
15602             }
15603         }
15604         
15605         return cfg;
15606     },
15607     
15608     initEvents:  function()
15609     {
15610         Roo.log('-------- init events on tab group ---------');
15611         
15612         if(this.bullets > 0 && !Roo.isTouch){
15613             this.initBullet();
15614         }
15615         
15616         Roo.log(this);
15617         
15618         if(Roo.isTouch && this.slideOnTouch){
15619             this.el.on("touchstart", this.onTouchStart, this);
15620         }
15621         
15622         if(this.autoslide){
15623             var _this = this;
15624             
15625             this.slideFn = window.setInterval(function() {
15626                 _this.showPanelNext();
15627             }, this.timer);
15628         }
15629         
15630     },
15631     
15632     onTouchStart : function(e, el, o)
15633     {
15634         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15635             return;
15636         }
15637         
15638         this.showPanelNext();
15639     },
15640     
15641     getChildContainer : function()
15642     {
15643         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15644     },
15645     
15646     /**
15647     * register a Navigation item
15648     * @param {Roo.bootstrap.NavItem} the navitem to add
15649     */
15650     register : function(item)
15651     {
15652         this.tabs.push( item);
15653         item.navId = this.navId; // not really needed..
15654     
15655     },
15656     
15657     getActivePanel : function()
15658     {
15659         var r = false;
15660         Roo.each(this.tabs, function(t) {
15661             if (t.active) {
15662                 r = t;
15663                 return false;
15664             }
15665             return null;
15666         });
15667         return r;
15668         
15669     },
15670     getPanelByName : function(n)
15671     {
15672         var r = false;
15673         Roo.each(this.tabs, function(t) {
15674             if (t.tabId == n) {
15675                 r = t;
15676                 return false;
15677             }
15678             return null;
15679         });
15680         return r;
15681     },
15682     indexOfPanel : function(p)
15683     {
15684         var r = false;
15685         Roo.each(this.tabs, function(t,i) {
15686             if (t.tabId == p.tabId) {
15687                 r = i;
15688                 return false;
15689             }
15690             return null;
15691         });
15692         return r;
15693     },
15694     /**
15695      * show a specific panel
15696      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15697      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15698      */
15699     showPanel : function (pan)
15700     {
15701         if(this.transition){
15702             Roo.log("waiting for the transitionend");
15703             return;
15704         }
15705         
15706         if (typeof(pan) == 'number') {
15707             pan = this.tabs[pan];
15708         }
15709         if (typeof(pan) == 'string') {
15710             pan = this.getPanelByName(pan);
15711         }
15712         if (pan.tabId == this.getActivePanel().tabId) {
15713             return true;
15714         }
15715         var cur = this.getActivePanel();
15716         
15717         if (false === cur.fireEvent('beforedeactivate')) {
15718             return false;
15719         }
15720         
15721         if(this.bullets > 0 && !Roo.isTouch){
15722             this.setActiveBullet(this.indexOfPanel(pan));
15723         }
15724         
15725         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15726             
15727             this.transition = true;
15728             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15729             var lr = dir == 'next' ? 'left' : 'right';
15730             pan.el.addClass(dir); // or prev
15731             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15732             cur.el.addClass(lr); // or right
15733             pan.el.addClass(lr);
15734             
15735             var _this = this;
15736             cur.el.on('transitionend', function() {
15737                 Roo.log("trans end?");
15738                 
15739                 pan.el.removeClass([lr,dir]);
15740                 pan.setActive(true);
15741                 
15742                 cur.el.removeClass([lr]);
15743                 cur.setActive(false);
15744                 
15745                 _this.transition = false;
15746                 
15747             }, this, { single:  true } );
15748             
15749             return true;
15750         }
15751         
15752         cur.setActive(false);
15753         pan.setActive(true);
15754         
15755         return true;
15756         
15757     },
15758     showPanelNext : function()
15759     {
15760         var i = this.indexOfPanel(this.getActivePanel());
15761         
15762         if (i >= this.tabs.length - 1 && !this.autoslide) {
15763             return;
15764         }
15765         
15766         if (i >= this.tabs.length - 1 && this.autoslide) {
15767             i = -1;
15768         }
15769         
15770         this.showPanel(this.tabs[i+1]);
15771     },
15772     
15773     showPanelPrev : function()
15774     {
15775         var i = this.indexOfPanel(this.getActivePanel());
15776         
15777         if (i  < 1 && !this.autoslide) {
15778             return;
15779         }
15780         
15781         if (i < 1 && this.autoslide) {
15782             i = this.tabs.length;
15783         }
15784         
15785         this.showPanel(this.tabs[i-1]);
15786     },
15787     
15788     initBullet : function()
15789     {
15790         if(Roo.isTouch){
15791             return;
15792         }
15793         
15794         var _this = this;
15795         
15796         for (var i = 0; i < this.bullets; i++){
15797             var bullet = this.el.select('.bullet-' + i, true).first();
15798
15799             if(!bullet){
15800                 continue;
15801             }
15802
15803             bullet.on('click', (function(e, el, o, ii, t){
15804
15805                 e.preventDefault();
15806
15807                 _this.showPanel(ii);
15808
15809                 if(_this.autoslide && _this.slideFn){
15810                     clearInterval(_this.slideFn);
15811                     _this.slideFn = window.setInterval(function() {
15812                         _this.showPanelNext();
15813                     }, _this.timer);
15814                 }
15815
15816             }).createDelegate(this, [i, bullet], true));
15817         }
15818     },
15819     
15820     setActiveBullet : function(i)
15821     {
15822         if(Roo.isTouch){
15823             return;
15824         }
15825         
15826         Roo.each(this.el.select('.bullet', true).elements, function(el){
15827             el.removeClass('selected');
15828         });
15829
15830         var bullet = this.el.select('.bullet-' + i, true).first();
15831         
15832         if(!bullet){
15833             return;
15834         }
15835         
15836         bullet.addClass('selected');
15837     }
15838     
15839     
15840   
15841 });
15842
15843  
15844
15845  
15846  
15847 Roo.apply(Roo.bootstrap.TabGroup, {
15848     
15849     groups: {},
15850      /**
15851     * register a Navigation Group
15852     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15853     */
15854     register : function(navgrp)
15855     {
15856         this.groups[navgrp.navId] = navgrp;
15857         
15858     },
15859     /**
15860     * fetch a Navigation Group based on the navigation ID
15861     * if one does not exist , it will get created.
15862     * @param {string} the navgroup to add
15863     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15864     */
15865     get: function(navId) {
15866         if (typeof(this.groups[navId]) == 'undefined') {
15867             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15868         }
15869         return this.groups[navId] ;
15870     }
15871     
15872     
15873     
15874 });
15875
15876  /*
15877  * - LGPL
15878  *
15879  * TabPanel
15880  * 
15881  */
15882
15883 /**
15884  * @class Roo.bootstrap.TabPanel
15885  * @extends Roo.bootstrap.Component
15886  * Bootstrap TabPanel class
15887  * @cfg {Boolean} active panel active
15888  * @cfg {String} html panel content
15889  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15890  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15891  * 
15892  * 
15893  * @constructor
15894  * Create a new TabPanel
15895  * @param {Object} config The config object
15896  */
15897
15898 Roo.bootstrap.TabPanel = function(config){
15899     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15900     this.addEvents({
15901         /**
15902              * @event changed
15903              * Fires when the active status changes
15904              * @param {Roo.bootstrap.TabPanel} this
15905              * @param {Boolean} state the new state
15906             
15907          */
15908         'changed': true,
15909         /**
15910              * @event beforedeactivate
15911              * Fires before a tab is de-activated - can be used to do validation on a form.
15912              * @param {Roo.bootstrap.TabPanel} this
15913              * @return {Boolean} false if there is an error
15914             
15915          */
15916         'beforedeactivate': true
15917      });
15918     
15919     this.tabId = this.tabId || Roo.id();
15920   
15921 };
15922
15923 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15924     
15925     active: false,
15926     html: false,
15927     tabId: false,
15928     navId : false,
15929     
15930     getAutoCreate : function(){
15931         var cfg = {
15932             tag: 'div',
15933             // item is needed for carousel - not sure if it has any effect otherwise
15934             cls: 'tab-pane item',
15935             html: this.html || ''
15936         };
15937         
15938         if(this.active){
15939             cfg.cls += ' active';
15940         }
15941         
15942         if(this.tabId){
15943             cfg.tabId = this.tabId;
15944         }
15945         
15946         
15947         return cfg;
15948     },
15949     
15950     initEvents:  function()
15951     {
15952         Roo.log('-------- init events on tab panel ---------');
15953         
15954         var p = this.parent();
15955         this.navId = this.navId || p.navId;
15956         
15957         if (typeof(this.navId) != 'undefined') {
15958             // not really needed.. but just in case.. parent should be a NavGroup.
15959             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15960             Roo.log(['register', tg, this]);
15961             tg.register(this);
15962             
15963             var i = tg.tabs.length - 1;
15964             
15965             if(this.active && tg.bullets > 0 && i < tg.bullets){
15966                 tg.setActiveBullet(i);
15967             }
15968         }
15969         
15970     },
15971     
15972     
15973     onRender : function(ct, position)
15974     {
15975        // Roo.log("Call onRender: " + this.xtype);
15976         
15977         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15978         
15979         
15980         
15981         
15982         
15983     },
15984     
15985     setActive: function(state)
15986     {
15987         Roo.log("panel - set active " + this.tabId + "=" + state);
15988         
15989         this.active = state;
15990         if (!state) {
15991             this.el.removeClass('active');
15992             
15993         } else  if (!this.el.hasClass('active')) {
15994             this.el.addClass('active');
15995         }
15996         
15997         this.fireEvent('changed', this, state);
15998     }
15999     
16000     
16001 });
16002  
16003
16004  
16005
16006  /*
16007  * - LGPL
16008  *
16009  * DateField
16010  * 
16011  */
16012
16013 /**
16014  * @class Roo.bootstrap.DateField
16015  * @extends Roo.bootstrap.Input
16016  * Bootstrap DateField class
16017  * @cfg {Number} weekStart default 0
16018  * @cfg {String} viewMode default empty, (months|years)
16019  * @cfg {String} minViewMode default empty, (months|years)
16020  * @cfg {Number} startDate default -Infinity
16021  * @cfg {Number} endDate default Infinity
16022  * @cfg {Boolean} todayHighlight default false
16023  * @cfg {Boolean} todayBtn default false
16024  * @cfg {Boolean} calendarWeeks default false
16025  * @cfg {Object} daysOfWeekDisabled default empty
16026  * @cfg {Boolean} singleMode default false (true | false)
16027  * 
16028  * @cfg {Boolean} keyboardNavigation default true
16029  * @cfg {String} language default en
16030  * 
16031  * @constructor
16032  * Create a new DateField
16033  * @param {Object} config The config object
16034  */
16035
16036 Roo.bootstrap.DateField = function(config){
16037     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16038      this.addEvents({
16039             /**
16040              * @event show
16041              * Fires when this field show.
16042              * @param {Roo.bootstrap.DateField} this
16043              * @param {Mixed} date The date value
16044              */
16045             show : true,
16046             /**
16047              * @event show
16048              * Fires when this field hide.
16049              * @param {Roo.bootstrap.DateField} this
16050              * @param {Mixed} date The date value
16051              */
16052             hide : true,
16053             /**
16054              * @event select
16055              * Fires when select a date.
16056              * @param {Roo.bootstrap.DateField} this
16057              * @param {Mixed} date The date value
16058              */
16059             select : true
16060         });
16061 };
16062
16063 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16064     
16065     /**
16066      * @cfg {String} format
16067      * The default date format string which can be overriden for localization support.  The format must be
16068      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16069      */
16070     format : "m/d/y",
16071     /**
16072      * @cfg {String} altFormats
16073      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16074      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16075      */
16076     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16077     
16078     weekStart : 0,
16079     
16080     viewMode : '',
16081     
16082     minViewMode : '',
16083     
16084     todayHighlight : false,
16085     
16086     todayBtn: false,
16087     
16088     language: 'en',
16089     
16090     keyboardNavigation: true,
16091     
16092     calendarWeeks: false,
16093     
16094     startDate: -Infinity,
16095     
16096     endDate: Infinity,
16097     
16098     daysOfWeekDisabled: [],
16099     
16100     _events: [],
16101     
16102     singleMode : false,
16103     
16104     UTCDate: function()
16105     {
16106         return new Date(Date.UTC.apply(Date, arguments));
16107     },
16108     
16109     UTCToday: function()
16110     {
16111         var today = new Date();
16112         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16113     },
16114     
16115     getDate: function() {
16116             var d = this.getUTCDate();
16117             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16118     },
16119     
16120     getUTCDate: function() {
16121             return this.date;
16122     },
16123     
16124     setDate: function(d) {
16125             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16126     },
16127     
16128     setUTCDate: function(d) {
16129             this.date = d;
16130             this.setValue(this.formatDate(this.date));
16131     },
16132         
16133     onRender: function(ct, position)
16134     {
16135         
16136         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16137         
16138         this.language = this.language || 'en';
16139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16140         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16141         
16142         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16143         this.format = this.format || 'm/d/y';
16144         this.isInline = false;
16145         this.isInput = true;
16146         this.component = this.el.select('.add-on', true).first() || false;
16147         this.component = (this.component && this.component.length === 0) ? false : this.component;
16148         this.hasInput = this.component && this.inputEL().length;
16149         
16150         if (typeof(this.minViewMode === 'string')) {
16151             switch (this.minViewMode) {
16152                 case 'months':
16153                     this.minViewMode = 1;
16154                     break;
16155                 case 'years':
16156                     this.minViewMode = 2;
16157                     break;
16158                 default:
16159                     this.minViewMode = 0;
16160                     break;
16161             }
16162         }
16163         
16164         if (typeof(this.viewMode === 'string')) {
16165             switch (this.viewMode) {
16166                 case 'months':
16167                     this.viewMode = 1;
16168                     break;
16169                 case 'years':
16170                     this.viewMode = 2;
16171                     break;
16172                 default:
16173                     this.viewMode = 0;
16174                     break;
16175             }
16176         }
16177                 
16178         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16179         
16180 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16181         
16182         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16183         
16184         this.picker().on('mousedown', this.onMousedown, this);
16185         this.picker().on('click', this.onClick, this);
16186         
16187         this.picker().addClass('datepicker-dropdown');
16188         
16189         this.startViewMode = this.viewMode;
16190         
16191         if(this.singleMode){
16192             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16193                 v.setVisibilityMode(Roo.Element.DISPLAY)
16194                 v.hide();
16195             });
16196             
16197             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16198                 v.setStyle('width', '189px');
16199             });
16200         }
16201         
16202         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16203             if(!this.calendarWeeks){
16204                 v.remove();
16205                 return;
16206             }
16207             
16208             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16209             v.attr('colspan', function(i, val){
16210                 return parseInt(val) + 1;
16211             });
16212         })
16213                         
16214         
16215         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16216         
16217         this.setStartDate(this.startDate);
16218         this.setEndDate(this.endDate);
16219         
16220         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16221         
16222         this.fillDow();
16223         this.fillMonths();
16224         this.update();
16225         this.showMode();
16226         
16227         if(this.isInline) {
16228             this.show();
16229         }
16230     },
16231     
16232     picker : function()
16233     {
16234         return this.pickerEl;
16235 //        return this.el.select('.datepicker', true).first();
16236     },
16237     
16238     fillDow: function()
16239     {
16240         var dowCnt = this.weekStart;
16241         
16242         var dow = {
16243             tag: 'tr',
16244             cn: [
16245                 
16246             ]
16247         };
16248         
16249         if(this.calendarWeeks){
16250             dow.cn.push({
16251                 tag: 'th',
16252                 cls: 'cw',
16253                 html: '&nbsp;'
16254             })
16255         }
16256         
16257         while (dowCnt < this.weekStart + 7) {
16258             dow.cn.push({
16259                 tag: 'th',
16260                 cls: 'dow',
16261                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16262             });
16263         }
16264         
16265         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16266     },
16267     
16268     fillMonths: function()
16269     {    
16270         var i = 0;
16271         var months = this.picker().select('>.datepicker-months td', true).first();
16272         
16273         months.dom.innerHTML = '';
16274         
16275         while (i < 12) {
16276             var month = {
16277                 tag: 'span',
16278                 cls: 'month',
16279                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16280             }
16281             
16282             months.createChild(month);
16283         }
16284         
16285     },
16286     
16287     update: function()
16288     {
16289         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;
16290         
16291         if (this.date < this.startDate) {
16292             this.viewDate = new Date(this.startDate);
16293         } else if (this.date > this.endDate) {
16294             this.viewDate = new Date(this.endDate);
16295         } else {
16296             this.viewDate = new Date(this.date);
16297         }
16298         
16299         this.fill();
16300     },
16301     
16302     fill: function() 
16303     {
16304         var d = new Date(this.viewDate),
16305                 year = d.getUTCFullYear(),
16306                 month = d.getUTCMonth(),
16307                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16308                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16309                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16310                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16311                 currentDate = this.date && this.date.valueOf(),
16312                 today = this.UTCToday();
16313         
16314         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16315         
16316 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16317         
16318 //        this.picker.select('>tfoot th.today').
16319 //                                              .text(dates[this.language].today)
16320 //                                              .toggle(this.todayBtn !== false);
16321     
16322         this.updateNavArrows();
16323         this.fillMonths();
16324                                                 
16325         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16326         
16327         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16328          
16329         prevMonth.setUTCDate(day);
16330         
16331         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16332         
16333         var nextMonth = new Date(prevMonth);
16334         
16335         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16336         
16337         nextMonth = nextMonth.valueOf();
16338         
16339         var fillMonths = false;
16340         
16341         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16342         
16343         while(prevMonth.valueOf() < nextMonth) {
16344             var clsName = '';
16345             
16346             if (prevMonth.getUTCDay() === this.weekStart) {
16347                 if(fillMonths){
16348                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16349                 }
16350                     
16351                 fillMonths = {
16352                     tag: 'tr',
16353                     cn: []
16354                 };
16355                 
16356                 if(this.calendarWeeks){
16357                     // ISO 8601: First week contains first thursday.
16358                     // ISO also states week starts on Monday, but we can be more abstract here.
16359                     var
16360                     // Start of current week: based on weekstart/current date
16361                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16362                     // Thursday of this week
16363                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16364                     // First Thursday of year, year from thursday
16365                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16366                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16367                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16368                     
16369                     fillMonths.cn.push({
16370                         tag: 'td',
16371                         cls: 'cw',
16372                         html: calWeek
16373                     });
16374                 }
16375             }
16376             
16377             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16378                 clsName += ' old';
16379             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16380                 clsName += ' new';
16381             }
16382             if (this.todayHighlight &&
16383                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16384                 prevMonth.getUTCMonth() == today.getMonth() &&
16385                 prevMonth.getUTCDate() == today.getDate()) {
16386                 clsName += ' today';
16387             }
16388             
16389             if (currentDate && prevMonth.valueOf() === currentDate) {
16390                 clsName += ' active';
16391             }
16392             
16393             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16394                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16395                     clsName += ' disabled';
16396             }
16397             
16398             fillMonths.cn.push({
16399                 tag: 'td',
16400                 cls: 'day ' + clsName,
16401                 html: prevMonth.getDate()
16402             })
16403             
16404             prevMonth.setDate(prevMonth.getDate()+1);
16405         }
16406           
16407         var currentYear = this.date && this.date.getUTCFullYear();
16408         var currentMonth = this.date && this.date.getUTCMonth();
16409         
16410         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16411         
16412         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16413             v.removeClass('active');
16414             
16415             if(currentYear === year && k === currentMonth){
16416                 v.addClass('active');
16417             }
16418             
16419             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16420                 v.addClass('disabled');
16421             }
16422             
16423         });
16424         
16425         
16426         year = parseInt(year/10, 10) * 10;
16427         
16428         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16429         
16430         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16431         
16432         year -= 1;
16433         for (var i = -1; i < 11; i++) {
16434             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16435                 tag: 'span',
16436                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16437                 html: year
16438             })
16439             
16440             year += 1;
16441         }
16442     },
16443     
16444     showMode: function(dir) 
16445     {
16446         if (dir) {
16447             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16448         }
16449         
16450         Roo.each(this.picker().select('>div',true).elements, function(v){
16451             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16452             v.hide();
16453         });
16454         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16455     },
16456     
16457     place: function()
16458     {
16459         if(this.isInline) return;
16460         
16461         this.picker().removeClass(['bottom', 'top']);
16462         
16463         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16464             /*
16465              * place to the top of element!
16466              *
16467              */
16468             
16469             this.picker().addClass('top');
16470             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16471             
16472             return;
16473         }
16474         
16475         this.picker().addClass('bottom');
16476         
16477         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16478     },
16479     
16480     parseDate : function(value)
16481     {
16482         if(!value || value instanceof Date){
16483             return value;
16484         }
16485         var v = Date.parseDate(value, this.format);
16486         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16487             v = Date.parseDate(value, 'Y-m-d');
16488         }
16489         if(!v && this.altFormats){
16490             if(!this.altFormatsArray){
16491                 this.altFormatsArray = this.altFormats.split("|");
16492             }
16493             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16494                 v = Date.parseDate(value, this.altFormatsArray[i]);
16495             }
16496         }
16497         return v;
16498     },
16499     
16500     formatDate : function(date, fmt)
16501     {   
16502         return (!date || !(date instanceof Date)) ?
16503         date : date.dateFormat(fmt || this.format);
16504     },
16505     
16506     onFocus : function()
16507     {
16508         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16509         this.show();
16510     },
16511     
16512     onBlur : function()
16513     {
16514         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16515         
16516         var d = this.inputEl().getValue();
16517         
16518         this.setValue(d);
16519                 
16520         this.hide();
16521     },
16522     
16523     show : function()
16524     {
16525         this.picker().show();
16526         this.update();
16527         this.place();
16528         
16529         this.fireEvent('show', this, this.date);
16530     },
16531     
16532     hide : function()
16533     {
16534         if(this.isInline) return;
16535         this.picker().hide();
16536         this.viewMode = this.startViewMode;
16537         this.showMode();
16538         
16539         this.fireEvent('hide', this, this.date);
16540         
16541     },
16542     
16543     onMousedown: function(e)
16544     {
16545         e.stopPropagation();
16546         e.preventDefault();
16547     },
16548     
16549     keyup: function(e)
16550     {
16551         Roo.bootstrap.DateField.superclass.keyup.call(this);
16552         this.update();
16553     },
16554
16555     setValue: function(v)
16556     {
16557         
16558         // v can be a string or a date..
16559         
16560         
16561         var d = new Date(this.parseDate(v) ).clearTime();
16562         
16563         if(isNaN(d.getTime())){
16564             this.date = this.viewDate = '';
16565             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16566             return;
16567         }
16568         
16569         v = this.formatDate(d);
16570         
16571         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16572         
16573         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16574      
16575         this.update();
16576
16577         this.fireEvent('select', this, this.date);
16578         
16579     },
16580     
16581     getValue: function()
16582     {
16583         return this.formatDate(this.date);
16584     },
16585     
16586     fireKey: function(e)
16587     {
16588         if (!this.picker().isVisible()){
16589             if (e.keyCode == 27) // allow escape to hide and re-show picker
16590                 this.show();
16591             return;
16592         }
16593         
16594         var dateChanged = false,
16595         dir, day, month,
16596         newDate, newViewDate;
16597         
16598         switch(e.keyCode){
16599             case 27: // escape
16600                 this.hide();
16601                 e.preventDefault();
16602                 break;
16603             case 37: // left
16604             case 39: // right
16605                 if (!this.keyboardNavigation) break;
16606                 dir = e.keyCode == 37 ? -1 : 1;
16607                 
16608                 if (e.ctrlKey){
16609                     newDate = this.moveYear(this.date, dir);
16610                     newViewDate = this.moveYear(this.viewDate, dir);
16611                 } else if (e.shiftKey){
16612                     newDate = this.moveMonth(this.date, dir);
16613                     newViewDate = this.moveMonth(this.viewDate, dir);
16614                 } else {
16615                     newDate = new Date(this.date);
16616                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16617                     newViewDate = new Date(this.viewDate);
16618                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16619                 }
16620                 if (this.dateWithinRange(newDate)){
16621                     this.date = newDate;
16622                     this.viewDate = newViewDate;
16623                     this.setValue(this.formatDate(this.date));
16624 //                    this.update();
16625                     e.preventDefault();
16626                     dateChanged = true;
16627                 }
16628                 break;
16629             case 38: // up
16630             case 40: // down
16631                 if (!this.keyboardNavigation) break;
16632                 dir = e.keyCode == 38 ? -1 : 1;
16633                 if (e.ctrlKey){
16634                     newDate = this.moveYear(this.date, dir);
16635                     newViewDate = this.moveYear(this.viewDate, dir);
16636                 } else if (e.shiftKey){
16637                     newDate = this.moveMonth(this.date, dir);
16638                     newViewDate = this.moveMonth(this.viewDate, dir);
16639                 } else {
16640                     newDate = new Date(this.date);
16641                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16642                     newViewDate = new Date(this.viewDate);
16643                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16644                 }
16645                 if (this.dateWithinRange(newDate)){
16646                     this.date = newDate;
16647                     this.viewDate = newViewDate;
16648                     this.setValue(this.formatDate(this.date));
16649 //                    this.update();
16650                     e.preventDefault();
16651                     dateChanged = true;
16652                 }
16653                 break;
16654             case 13: // enter
16655                 this.setValue(this.formatDate(this.date));
16656                 this.hide();
16657                 e.preventDefault();
16658                 break;
16659             case 9: // tab
16660                 this.setValue(this.formatDate(this.date));
16661                 this.hide();
16662                 break;
16663             case 16: // shift
16664             case 17: // ctrl
16665             case 18: // alt
16666                 break;
16667             default :
16668                 this.hide();
16669                 
16670         }
16671     },
16672     
16673     
16674     onClick: function(e) 
16675     {
16676         e.stopPropagation();
16677         e.preventDefault();
16678         
16679         var target = e.getTarget();
16680         
16681         if(target.nodeName.toLowerCase() === 'i'){
16682             target = Roo.get(target).dom.parentNode;
16683         }
16684         
16685         var nodeName = target.nodeName;
16686         var className = target.className;
16687         var html = target.innerHTML;
16688         //Roo.log(nodeName);
16689         
16690         switch(nodeName.toLowerCase()) {
16691             case 'th':
16692                 switch(className) {
16693                     case 'switch':
16694                         this.showMode(1);
16695                         break;
16696                     case 'prev':
16697                     case 'next':
16698                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16699                         switch(this.viewMode){
16700                                 case 0:
16701                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16702                                         break;
16703                                 case 1:
16704                                 case 2:
16705                                         this.viewDate = this.moveYear(this.viewDate, dir);
16706                                         break;
16707                         }
16708                         this.fill();
16709                         break;
16710                     case 'today':
16711                         var date = new Date();
16712                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16713 //                        this.fill()
16714                         this.setValue(this.formatDate(this.date));
16715                         
16716                         this.hide();
16717                         break;
16718                 }
16719                 break;
16720             case 'span':
16721                 if (className.indexOf('disabled') < 0) {
16722                     this.viewDate.setUTCDate(1);
16723                     if (className.indexOf('month') > -1) {
16724                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16725                     } else {
16726                         var year = parseInt(html, 10) || 0;
16727                         this.viewDate.setUTCFullYear(year);
16728                         
16729                     }
16730                     
16731                     if(this.singleMode){
16732                         this.setValue(this.formatDate(this.viewDate));
16733                         this.hide();
16734                         return;
16735                     }
16736                     
16737                     this.showMode(-1);
16738                     this.fill();
16739                 }
16740                 break;
16741                 
16742             case 'td':
16743                 //Roo.log(className);
16744                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16745                     var day = parseInt(html, 10) || 1;
16746                     var year = this.viewDate.getUTCFullYear(),
16747                         month = this.viewDate.getUTCMonth();
16748
16749                     if (className.indexOf('old') > -1) {
16750                         if(month === 0 ){
16751                             month = 11;
16752                             year -= 1;
16753                         }else{
16754                             month -= 1;
16755                         }
16756                     } else if (className.indexOf('new') > -1) {
16757                         if (month == 11) {
16758                             month = 0;
16759                             year += 1;
16760                         } else {
16761                             month += 1;
16762                         }
16763                     }
16764                     //Roo.log([year,month,day]);
16765                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16766                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16767 //                    this.fill();
16768                     //Roo.log(this.formatDate(this.date));
16769                     this.setValue(this.formatDate(this.date));
16770                     this.hide();
16771                 }
16772                 break;
16773         }
16774     },
16775     
16776     setStartDate: function(startDate)
16777     {
16778         this.startDate = startDate || -Infinity;
16779         if (this.startDate !== -Infinity) {
16780             this.startDate = this.parseDate(this.startDate);
16781         }
16782         this.update();
16783         this.updateNavArrows();
16784     },
16785
16786     setEndDate: function(endDate)
16787     {
16788         this.endDate = endDate || Infinity;
16789         if (this.endDate !== Infinity) {
16790             this.endDate = this.parseDate(this.endDate);
16791         }
16792         this.update();
16793         this.updateNavArrows();
16794     },
16795     
16796     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16797     {
16798         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16799         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16800             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16801         }
16802         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16803             return parseInt(d, 10);
16804         });
16805         this.update();
16806         this.updateNavArrows();
16807     },
16808     
16809     updateNavArrows: function() 
16810     {
16811         if(this.singleMode){
16812             return;
16813         }
16814         
16815         var d = new Date(this.viewDate),
16816         year = d.getUTCFullYear(),
16817         month = d.getUTCMonth();
16818         
16819         Roo.each(this.picker().select('.prev', true).elements, function(v){
16820             v.show();
16821             switch (this.viewMode) {
16822                 case 0:
16823
16824                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16825                         v.hide();
16826                     }
16827                     break;
16828                 case 1:
16829                 case 2:
16830                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16831                         v.hide();
16832                     }
16833                     break;
16834             }
16835         });
16836         
16837         Roo.each(this.picker().select('.next', true).elements, function(v){
16838             v.show();
16839             switch (this.viewMode) {
16840                 case 0:
16841
16842                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16843                         v.hide();
16844                     }
16845                     break;
16846                 case 1:
16847                 case 2:
16848                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16849                         v.hide();
16850                     }
16851                     break;
16852             }
16853         })
16854     },
16855     
16856     moveMonth: function(date, dir)
16857     {
16858         if (!dir) return date;
16859         var new_date = new Date(date.valueOf()),
16860         day = new_date.getUTCDate(),
16861         month = new_date.getUTCMonth(),
16862         mag = Math.abs(dir),
16863         new_month, test;
16864         dir = dir > 0 ? 1 : -1;
16865         if (mag == 1){
16866             test = dir == -1
16867             // If going back one month, make sure month is not current month
16868             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16869             ? function(){
16870                 return new_date.getUTCMonth() == month;
16871             }
16872             // If going forward one month, make sure month is as expected
16873             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16874             : function(){
16875                 return new_date.getUTCMonth() != new_month;
16876             };
16877             new_month = month + dir;
16878             new_date.setUTCMonth(new_month);
16879             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16880             if (new_month < 0 || new_month > 11)
16881                 new_month = (new_month + 12) % 12;
16882         } else {
16883             // For magnitudes >1, move one month at a time...
16884             for (var i=0; i<mag; i++)
16885                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16886                 new_date = this.moveMonth(new_date, dir);
16887             // ...then reset the day, keeping it in the new month
16888             new_month = new_date.getUTCMonth();
16889             new_date.setUTCDate(day);
16890             test = function(){
16891                 return new_month != new_date.getUTCMonth();
16892             };
16893         }
16894         // Common date-resetting loop -- if date is beyond end of month, make it
16895         // end of month
16896         while (test()){
16897             new_date.setUTCDate(--day);
16898             new_date.setUTCMonth(new_month);
16899         }
16900         return new_date;
16901     },
16902
16903     moveYear: function(date, dir)
16904     {
16905         return this.moveMonth(date, dir*12);
16906     },
16907
16908     dateWithinRange: function(date)
16909     {
16910         return date >= this.startDate && date <= this.endDate;
16911     },
16912
16913     
16914     remove: function() 
16915     {
16916         this.picker().remove();
16917     }
16918    
16919 });
16920
16921 Roo.apply(Roo.bootstrap.DateField,  {
16922     
16923     head : {
16924         tag: 'thead',
16925         cn: [
16926         {
16927             tag: 'tr',
16928             cn: [
16929             {
16930                 tag: 'th',
16931                 cls: 'prev',
16932                 html: '<i class="fa fa-arrow-left"/>'
16933             },
16934             {
16935                 tag: 'th',
16936                 cls: 'switch',
16937                 colspan: '5'
16938             },
16939             {
16940                 tag: 'th',
16941                 cls: 'next',
16942                 html: '<i class="fa fa-arrow-right"/>'
16943             }
16944
16945             ]
16946         }
16947         ]
16948     },
16949     
16950     content : {
16951         tag: 'tbody',
16952         cn: [
16953         {
16954             tag: 'tr',
16955             cn: [
16956             {
16957                 tag: 'td',
16958                 colspan: '7'
16959             }
16960             ]
16961         }
16962         ]
16963     },
16964     
16965     footer : {
16966         tag: 'tfoot',
16967         cn: [
16968         {
16969             tag: 'tr',
16970             cn: [
16971             {
16972                 tag: 'th',
16973                 colspan: '7',
16974                 cls: 'today'
16975             }
16976                     
16977             ]
16978         }
16979         ]
16980     },
16981     
16982     dates:{
16983         en: {
16984             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16985             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16986             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16987             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16988             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16989             today: "Today"
16990         }
16991     },
16992     
16993     modes: [
16994     {
16995         clsName: 'days',
16996         navFnc: 'Month',
16997         navStep: 1
16998     },
16999     {
17000         clsName: 'months',
17001         navFnc: 'FullYear',
17002         navStep: 1
17003     },
17004     {
17005         clsName: 'years',
17006         navFnc: 'FullYear',
17007         navStep: 10
17008     }]
17009 });
17010
17011 Roo.apply(Roo.bootstrap.DateField,  {
17012   
17013     template : {
17014         tag: 'div',
17015         cls: 'datepicker dropdown-menu roo-dynamic',
17016         cn: [
17017         {
17018             tag: 'div',
17019             cls: 'datepicker-days',
17020             cn: [
17021             {
17022                 tag: 'table',
17023                 cls: 'table-condensed',
17024                 cn:[
17025                 Roo.bootstrap.DateField.head,
17026                 {
17027                     tag: 'tbody'
17028                 },
17029                 Roo.bootstrap.DateField.footer
17030                 ]
17031             }
17032             ]
17033         },
17034         {
17035             tag: 'div',
17036             cls: 'datepicker-months',
17037             cn: [
17038             {
17039                 tag: 'table',
17040                 cls: 'table-condensed',
17041                 cn:[
17042                 Roo.bootstrap.DateField.head,
17043                 Roo.bootstrap.DateField.content,
17044                 Roo.bootstrap.DateField.footer
17045                 ]
17046             }
17047             ]
17048         },
17049         {
17050             tag: 'div',
17051             cls: 'datepicker-years',
17052             cn: [
17053             {
17054                 tag: 'table',
17055                 cls: 'table-condensed',
17056                 cn:[
17057                 Roo.bootstrap.DateField.head,
17058                 Roo.bootstrap.DateField.content,
17059                 Roo.bootstrap.DateField.footer
17060                 ]
17061             }
17062             ]
17063         }
17064         ]
17065     }
17066 });
17067
17068  
17069
17070  /*
17071  * - LGPL
17072  *
17073  * TimeField
17074  * 
17075  */
17076
17077 /**
17078  * @class Roo.bootstrap.TimeField
17079  * @extends Roo.bootstrap.Input
17080  * Bootstrap DateField class
17081  * 
17082  * 
17083  * @constructor
17084  * Create a new TimeField
17085  * @param {Object} config The config object
17086  */
17087
17088 Roo.bootstrap.TimeField = function(config){
17089     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17090     this.addEvents({
17091             /**
17092              * @event show
17093              * Fires when this field show.
17094              * @param {Roo.bootstrap.DateField} thisthis
17095              * @param {Mixed} date The date value
17096              */
17097             show : true,
17098             /**
17099              * @event show
17100              * Fires when this field hide.
17101              * @param {Roo.bootstrap.DateField} this
17102              * @param {Mixed} date The date value
17103              */
17104             hide : true,
17105             /**
17106              * @event select
17107              * Fires when select a date.
17108              * @param {Roo.bootstrap.DateField} this
17109              * @param {Mixed} date The date value
17110              */
17111             select : true
17112         });
17113 };
17114
17115 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17116     
17117     /**
17118      * @cfg {String} format
17119      * The default time format string which can be overriden for localization support.  The format must be
17120      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17121      */
17122     format : "H:i",
17123        
17124     onRender: function(ct, position)
17125     {
17126         
17127         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17128                 
17129         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17130         
17131         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17132         
17133         this.pop = this.picker().select('>.datepicker-time',true).first();
17134         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17135         
17136         this.picker().on('mousedown', this.onMousedown, this);
17137         this.picker().on('click', this.onClick, this);
17138         
17139         this.picker().addClass('datepicker-dropdown');
17140     
17141         this.fillTime();
17142         this.update();
17143             
17144         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17145         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17146         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17147         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17148         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17149         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17150
17151     },
17152     
17153     fireKey: function(e){
17154         if (!this.picker().isVisible()){
17155             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17156                 this.show();
17157             }
17158             return;
17159         }
17160
17161         e.preventDefault();
17162         
17163         switch(e.keyCode){
17164             case 27: // escape
17165                 this.hide();
17166                 break;
17167             case 37: // left
17168             case 39: // right
17169                 this.onTogglePeriod();
17170                 break;
17171             case 38: // up
17172                 this.onIncrementMinutes();
17173                 break;
17174             case 40: // down
17175                 this.onDecrementMinutes();
17176                 break;
17177             case 13: // enter
17178             case 9: // tab
17179                 this.setTime();
17180                 break;
17181         }
17182     },
17183     
17184     onClick: function(e) {
17185         e.stopPropagation();
17186         e.preventDefault();
17187     },
17188     
17189     picker : function()
17190     {
17191         return this.el.select('.datepicker', true).first();
17192     },
17193     
17194     fillTime: function()
17195     {    
17196         var time = this.pop.select('tbody', true).first();
17197         
17198         time.dom.innerHTML = '';
17199         
17200         time.createChild({
17201             tag: 'tr',
17202             cn: [
17203                 {
17204                     tag: 'td',
17205                     cn: [
17206                         {
17207                             tag: 'a',
17208                             href: '#',
17209                             cls: 'btn',
17210                             cn: [
17211                                 {
17212                                     tag: 'span',
17213                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17214                                 }
17215                             ]
17216                         } 
17217                     ]
17218                 },
17219                 {
17220                     tag: 'td',
17221                     cls: 'separator'
17222                 },
17223                 {
17224                     tag: 'td',
17225                     cn: [
17226                         {
17227                             tag: 'a',
17228                             href: '#',
17229                             cls: 'btn',
17230                             cn: [
17231                                 {
17232                                     tag: 'span',
17233                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17234                                 }
17235                             ]
17236                         }
17237                     ]
17238                 },
17239                 {
17240                     tag: 'td',
17241                     cls: 'separator'
17242                 }
17243             ]
17244         });
17245         
17246         time.createChild({
17247             tag: 'tr',
17248             cn: [
17249                 {
17250                     tag: 'td',
17251                     cn: [
17252                         {
17253                             tag: 'span',
17254                             cls: 'timepicker-hour',
17255                             html: '00'
17256                         }  
17257                     ]
17258                 },
17259                 {
17260                     tag: 'td',
17261                     cls: 'separator',
17262                     html: ':'
17263                 },
17264                 {
17265                     tag: 'td',
17266                     cn: [
17267                         {
17268                             tag: 'span',
17269                             cls: 'timepicker-minute',
17270                             html: '00'
17271                         }  
17272                     ]
17273                 },
17274                 {
17275                     tag: 'td',
17276                     cls: 'separator'
17277                 },
17278                 {
17279                     tag: 'td',
17280                     cn: [
17281                         {
17282                             tag: 'button',
17283                             type: 'button',
17284                             cls: 'btn btn-primary period',
17285                             html: 'AM'
17286                             
17287                         }
17288                     ]
17289                 }
17290             ]
17291         });
17292         
17293         time.createChild({
17294             tag: 'tr',
17295             cn: [
17296                 {
17297                     tag: 'td',
17298                     cn: [
17299                         {
17300                             tag: 'a',
17301                             href: '#',
17302                             cls: 'btn',
17303                             cn: [
17304                                 {
17305                                     tag: 'span',
17306                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17307                                 }
17308                             ]
17309                         }
17310                     ]
17311                 },
17312                 {
17313                     tag: 'td',
17314                     cls: 'separator'
17315                 },
17316                 {
17317                     tag: 'td',
17318                     cn: [
17319                         {
17320                             tag: 'a',
17321                             href: '#',
17322                             cls: 'btn',
17323                             cn: [
17324                                 {
17325                                     tag: 'span',
17326                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17327                                 }
17328                             ]
17329                         }
17330                     ]
17331                 },
17332                 {
17333                     tag: 'td',
17334                     cls: 'separator'
17335                 }
17336             ]
17337         });
17338         
17339     },
17340     
17341     update: function()
17342     {
17343         
17344         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17345         
17346         this.fill();
17347     },
17348     
17349     fill: function() 
17350     {
17351         var hours = this.time.getHours();
17352         var minutes = this.time.getMinutes();
17353         var period = 'AM';
17354         
17355         if(hours > 11){
17356             period = 'PM';
17357         }
17358         
17359         if(hours == 0){
17360             hours = 12;
17361         }
17362         
17363         
17364         if(hours > 12){
17365             hours = hours - 12;
17366         }
17367         
17368         if(hours < 10){
17369             hours = '0' + hours;
17370         }
17371         
17372         if(minutes < 10){
17373             minutes = '0' + minutes;
17374         }
17375         
17376         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17377         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17378         this.pop.select('button', true).first().dom.innerHTML = period;
17379         
17380     },
17381     
17382     place: function()
17383     {   
17384         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17385         
17386         var cls = ['bottom'];
17387         
17388         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17389             cls.pop();
17390             cls.push('top');
17391         }
17392         
17393         cls.push('right');
17394         
17395         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17396             cls.pop();
17397             cls.push('left');
17398         }
17399         
17400         this.picker().addClass(cls.join('-'));
17401         
17402         var _this = this;
17403         
17404         Roo.each(cls, function(c){
17405             if(c == 'bottom'){
17406                 _this.picker().setTop(_this.inputEl().getHeight());
17407                 return;
17408             }
17409             if(c == 'top'){
17410                 _this.picker().setTop(0 - _this.picker().getHeight());
17411                 return;
17412             }
17413             
17414             if(c == 'left'){
17415                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17416                 return;
17417             }
17418             if(c == 'right'){
17419                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17420                 return;
17421             }
17422         });
17423         
17424     },
17425   
17426     onFocus : function()
17427     {
17428         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17429         this.show();
17430     },
17431     
17432     onBlur : function()
17433     {
17434         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17435         this.hide();
17436     },
17437     
17438     show : function()
17439     {
17440         this.picker().show();
17441         this.pop.show();
17442         this.update();
17443         this.place();
17444         
17445         this.fireEvent('show', this, this.date);
17446     },
17447     
17448     hide : function()
17449     {
17450         this.picker().hide();
17451         this.pop.hide();
17452         
17453         this.fireEvent('hide', this, this.date);
17454     },
17455     
17456     setTime : function()
17457     {
17458         this.hide();
17459         this.setValue(this.time.format(this.format));
17460         
17461         this.fireEvent('select', this, this.date);
17462         
17463         
17464     },
17465     
17466     onMousedown: function(e){
17467         e.stopPropagation();
17468         e.preventDefault();
17469     },
17470     
17471     onIncrementHours: function()
17472     {
17473         Roo.log('onIncrementHours');
17474         this.time = this.time.add(Date.HOUR, 1);
17475         this.update();
17476         
17477     },
17478     
17479     onDecrementHours: function()
17480     {
17481         Roo.log('onDecrementHours');
17482         this.time = this.time.add(Date.HOUR, -1);
17483         this.update();
17484     },
17485     
17486     onIncrementMinutes: function()
17487     {
17488         Roo.log('onIncrementMinutes');
17489         this.time = this.time.add(Date.MINUTE, 1);
17490         this.update();
17491     },
17492     
17493     onDecrementMinutes: function()
17494     {
17495         Roo.log('onDecrementMinutes');
17496         this.time = this.time.add(Date.MINUTE, -1);
17497         this.update();
17498     },
17499     
17500     onTogglePeriod: function()
17501     {
17502         Roo.log('onTogglePeriod');
17503         this.time = this.time.add(Date.HOUR, 12);
17504         this.update();
17505     }
17506     
17507    
17508 });
17509
17510 Roo.apply(Roo.bootstrap.TimeField,  {
17511     
17512     content : {
17513         tag: 'tbody',
17514         cn: [
17515             {
17516                 tag: 'tr',
17517                 cn: [
17518                 {
17519                     tag: 'td',
17520                     colspan: '7'
17521                 }
17522                 ]
17523             }
17524         ]
17525     },
17526     
17527     footer : {
17528         tag: 'tfoot',
17529         cn: [
17530             {
17531                 tag: 'tr',
17532                 cn: [
17533                 {
17534                     tag: 'th',
17535                     colspan: '7',
17536                     cls: '',
17537                     cn: [
17538                         {
17539                             tag: 'button',
17540                             cls: 'btn btn-info ok',
17541                             html: 'OK'
17542                         }
17543                     ]
17544                 }
17545
17546                 ]
17547             }
17548         ]
17549     }
17550 });
17551
17552 Roo.apply(Roo.bootstrap.TimeField,  {
17553   
17554     template : {
17555         tag: 'div',
17556         cls: 'datepicker dropdown-menu',
17557         cn: [
17558             {
17559                 tag: 'div',
17560                 cls: 'datepicker-time',
17561                 cn: [
17562                 {
17563                     tag: 'table',
17564                     cls: 'table-condensed',
17565                     cn:[
17566                     Roo.bootstrap.TimeField.content,
17567                     Roo.bootstrap.TimeField.footer
17568                     ]
17569                 }
17570                 ]
17571             }
17572         ]
17573     }
17574 });
17575
17576  
17577
17578  /*
17579  * - LGPL
17580  *
17581  * MonthField
17582  * 
17583  */
17584
17585 /**
17586  * @class Roo.bootstrap.MonthField
17587  * @extends Roo.bootstrap.Input
17588  * Bootstrap MonthField class
17589  * 
17590  * @cfg {String} language default en
17591  * 
17592  * @constructor
17593  * Create a new MonthField
17594  * @param {Object} config The config object
17595  */
17596
17597 Roo.bootstrap.MonthField = function(config){
17598     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17599     
17600     this.addEvents({
17601         /**
17602          * @event show
17603          * Fires when this field show.
17604          * @param {Roo.bootstrap.MonthField} this
17605          * @param {Mixed} date The date value
17606          */
17607         show : true,
17608         /**
17609          * @event show
17610          * Fires when this field hide.
17611          * @param {Roo.bootstrap.MonthField} this
17612          * @param {Mixed} date The date value
17613          */
17614         hide : true,
17615         /**
17616          * @event select
17617          * Fires when select a date.
17618          * @param {Roo.bootstrap.MonthField} this
17619          * @param {String} oldvalue The old value
17620          * @param {String} newvalue The new value
17621          */
17622         select : true
17623     });
17624 };
17625
17626 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17627     
17628     onRender: function(ct, position)
17629     {
17630         
17631         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17632         
17633         this.language = this.language || 'en';
17634         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17635         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17636         
17637         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17638         this.isInline = false;
17639         this.isInput = true;
17640         this.component = this.el.select('.add-on', true).first() || false;
17641         this.component = (this.component && this.component.length === 0) ? false : this.component;
17642         this.hasInput = this.component && this.inputEL().length;
17643         
17644         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17645         
17646         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17647         
17648         this.picker().on('mousedown', this.onMousedown, this);
17649         this.picker().on('click', this.onClick, this);
17650         
17651         this.picker().addClass('datepicker-dropdown');
17652         
17653         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17654             v.setStyle('width', '189px');
17655         });
17656         
17657         this.fillMonths();
17658         
17659         this.update();
17660         
17661         if(this.isInline) {
17662             this.show();
17663         }
17664         
17665     },
17666     
17667     setValue: function(v, suppressEvent)
17668     {   
17669         var o = this.getValue();
17670         
17671         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17672         
17673         this.update();
17674
17675         if(suppressEvent !== true){
17676             this.fireEvent('select', this, o, v);
17677         }
17678         
17679     },
17680     
17681     getValue: function()
17682     {
17683         return this.value;
17684     },
17685     
17686     onClick: function(e) 
17687     {
17688         e.stopPropagation();
17689         e.preventDefault();
17690         
17691         var target = e.getTarget();
17692         
17693         if(target.nodeName.toLowerCase() === 'i'){
17694             target = Roo.get(target).dom.parentNode;
17695         }
17696         
17697         var nodeName = target.nodeName;
17698         var className = target.className;
17699         var html = target.innerHTML;
17700         
17701         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17702             return;
17703         }
17704         
17705         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17706         
17707         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17708         
17709         this.hide();
17710                         
17711     },
17712     
17713     picker : function()
17714     {
17715         return this.pickerEl;
17716     },
17717     
17718     fillMonths: function()
17719     {    
17720         var i = 0;
17721         var months = this.picker().select('>.datepicker-months td', true).first();
17722         
17723         months.dom.innerHTML = '';
17724         
17725         while (i < 12) {
17726             var month = {
17727                 tag: 'span',
17728                 cls: 'month',
17729                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17730             }
17731             
17732             months.createChild(month);
17733         }
17734         
17735     },
17736     
17737     update: function()
17738     {
17739         var _this = this;
17740         
17741         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17742             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17743         }
17744         
17745         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17746             e.removeClass('active');
17747             
17748             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17749                 e.addClass('active');
17750             }
17751         })
17752     },
17753     
17754     place: function()
17755     {
17756         if(this.isInline) return;
17757         
17758         this.picker().removeClass(['bottom', 'top']);
17759         
17760         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17761             /*
17762              * place to the top of element!
17763              *
17764              */
17765             
17766             this.picker().addClass('top');
17767             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17768             
17769             return;
17770         }
17771         
17772         this.picker().addClass('bottom');
17773         
17774         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17775     },
17776     
17777     onFocus : function()
17778     {
17779         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17780         this.show();
17781     },
17782     
17783     onBlur : function()
17784     {
17785         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17786         
17787         var d = this.inputEl().getValue();
17788         
17789         this.setValue(d);
17790                 
17791         this.hide();
17792     },
17793     
17794     show : function()
17795     {
17796         this.picker().show();
17797         this.picker().select('>.datepicker-months', true).first().show();
17798         this.update();
17799         this.place();
17800         
17801         this.fireEvent('show', this, this.date);
17802     },
17803     
17804     hide : function()
17805     {
17806         if(this.isInline) return;
17807         this.picker().hide();
17808         this.fireEvent('hide', this, this.date);
17809         
17810     },
17811     
17812     onMousedown: function(e)
17813     {
17814         e.stopPropagation();
17815         e.preventDefault();
17816     },
17817     
17818     keyup: function(e)
17819     {
17820         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17821         this.update();
17822     },
17823
17824     fireKey: function(e)
17825     {
17826         if (!this.picker().isVisible()){
17827             if (e.keyCode == 27) // allow escape to hide and re-show picker
17828                 this.show();
17829             return;
17830         }
17831         
17832         var dir;
17833         
17834         switch(e.keyCode){
17835             case 27: // escape
17836                 this.hide();
17837                 e.preventDefault();
17838                 break;
17839             case 37: // left
17840             case 39: // right
17841                 dir = e.keyCode == 37 ? -1 : 1;
17842                 
17843                 this.vIndex = this.vIndex + dir;
17844                 
17845                 if(this.vIndex < 0){
17846                     this.vIndex = 0;
17847                 }
17848                 
17849                 if(this.vIndex > 11){
17850                     this.vIndex = 11;
17851                 }
17852                 
17853                 if(isNaN(this.vIndex)){
17854                     this.vIndex = 0;
17855                 }
17856                 
17857                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17858                 
17859                 break;
17860             case 38: // up
17861             case 40: // down
17862                 
17863                 dir = e.keyCode == 38 ? -1 : 1;
17864                 
17865                 this.vIndex = this.vIndex + dir * 4;
17866                 
17867                 if(this.vIndex < 0){
17868                     this.vIndex = 0;
17869                 }
17870                 
17871                 if(this.vIndex > 11){
17872                     this.vIndex = 11;
17873                 }
17874                 
17875                 if(isNaN(this.vIndex)){
17876                     this.vIndex = 0;
17877                 }
17878                 
17879                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17880                 break;
17881                 
17882             case 13: // enter
17883                 
17884                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17885                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17886                 }
17887                 
17888                 this.hide();
17889                 e.preventDefault();
17890                 break;
17891             case 9: // tab
17892                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17893                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17894                 }
17895                 this.hide();
17896                 break;
17897             case 16: // shift
17898             case 17: // ctrl
17899             case 18: // alt
17900                 break;
17901             default :
17902                 this.hide();
17903                 
17904         }
17905     },
17906     
17907     remove: function() 
17908     {
17909         this.picker().remove();
17910     }
17911    
17912 });
17913
17914 Roo.apply(Roo.bootstrap.MonthField,  {
17915     
17916     content : {
17917         tag: 'tbody',
17918         cn: [
17919         {
17920             tag: 'tr',
17921             cn: [
17922             {
17923                 tag: 'td',
17924                 colspan: '7'
17925             }
17926             ]
17927         }
17928         ]
17929     },
17930     
17931     dates:{
17932         en: {
17933             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17934             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17935         }
17936     }
17937 });
17938
17939 Roo.apply(Roo.bootstrap.MonthField,  {
17940   
17941     template : {
17942         tag: 'div',
17943         cls: 'datepicker dropdown-menu roo-dynamic',
17944         cn: [
17945             {
17946                 tag: 'div',
17947                 cls: 'datepicker-months',
17948                 cn: [
17949                 {
17950                     tag: 'table',
17951                     cls: 'table-condensed',
17952                     cn:[
17953                         Roo.bootstrap.DateField.content
17954                     ]
17955                 }
17956                 ]
17957             }
17958         ]
17959     }
17960 });
17961
17962  
17963
17964  
17965  /*
17966  * - LGPL
17967  *
17968  * CheckBox
17969  * 
17970  */
17971
17972 /**
17973  * @class Roo.bootstrap.CheckBox
17974  * @extends Roo.bootstrap.Input
17975  * Bootstrap CheckBox class
17976  * 
17977  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17978  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17979  * @cfg {String} boxLabel The text that appears beside the checkbox
17980  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17981  * @cfg {Boolean} checked initnal the element
17982  * @cfg {Boolean} inline inline the element (default false)
17983  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17984  * 
17985  * @constructor
17986  * Create a new CheckBox
17987  * @param {Object} config The config object
17988  */
17989
17990 Roo.bootstrap.CheckBox = function(config){
17991     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17992    
17993     this.addEvents({
17994         /**
17995         * @event check
17996         * Fires when the element is checked or unchecked.
17997         * @param {Roo.bootstrap.CheckBox} this This input
17998         * @param {Boolean} checked The new checked value
17999         */
18000        check : true
18001     });
18002     
18003 };
18004
18005 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18006   
18007     inputType: 'checkbox',
18008     inputValue: 1,
18009     valueOff: 0,
18010     boxLabel: false,
18011     checked: false,
18012     weight : false,
18013     inline: false,
18014     
18015     getAutoCreate : function()
18016     {
18017         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18018         
18019         var id = Roo.id();
18020         
18021         var cfg = {};
18022         
18023         cfg.cls = 'form-group ' + this.inputType; //input-group
18024         
18025         if(this.inline){
18026             cfg.cls += ' ' + this.inputType + '-inline';
18027         }
18028         
18029         var input =  {
18030             tag: 'input',
18031             id : id,
18032             type : this.inputType,
18033             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18034             cls : 'roo-' + this.inputType, //'form-box',
18035             placeholder : this.placeholder || ''
18036             
18037         };
18038         
18039         if (this.weight) { // Validity check?
18040             cfg.cls += " " + this.inputType + "-" + this.weight;
18041         }
18042         
18043         if (this.disabled) {
18044             input.disabled=true;
18045         }
18046         
18047         if(this.checked){
18048             input.checked = this.checked;
18049         }
18050         
18051         if (this.name) {
18052             input.name = this.name;
18053         }
18054         
18055         if (this.size) {
18056             input.cls += ' input-' + this.size;
18057         }
18058         
18059         var settings=this;
18060         
18061         ['xs','sm','md','lg'].map(function(size){
18062             if (settings[size]) {
18063                 cfg.cls += ' col-' + size + '-' + settings[size];
18064             }
18065         });
18066         
18067         var inputblock = input;
18068          
18069         if (this.before || this.after) {
18070             
18071             inputblock = {
18072                 cls : 'input-group',
18073                 cn :  [] 
18074             };
18075             
18076             if (this.before) {
18077                 inputblock.cn.push({
18078                     tag :'span',
18079                     cls : 'input-group-addon',
18080                     html : this.before
18081                 });
18082             }
18083             
18084             inputblock.cn.push(input);
18085             
18086             if (this.after) {
18087                 inputblock.cn.push({
18088                     tag :'span',
18089                     cls : 'input-group-addon',
18090                     html : this.after
18091                 });
18092             }
18093             
18094         }
18095         
18096         if (align ==='left' && this.fieldLabel.length) {
18097                 Roo.log("left and has label");
18098                 cfg.cn = [
18099                     
18100                     {
18101                         tag: 'label',
18102                         'for' :  id,
18103                         cls : 'control-label col-md-' + this.labelWidth,
18104                         html : this.fieldLabel
18105                         
18106                     },
18107                     {
18108                         cls : "col-md-" + (12 - this.labelWidth), 
18109                         cn: [
18110                             inputblock
18111                         ]
18112                     }
18113                     
18114                 ];
18115         } else if ( this.fieldLabel.length) {
18116                 Roo.log(" label");
18117                 cfg.cn = [
18118                    
18119                     {
18120                         tag: this.boxLabel ? 'span' : 'label',
18121                         'for': id,
18122                         cls: 'control-label box-input-label',
18123                         //cls : 'input-group-addon',
18124                         html : this.fieldLabel
18125                         
18126                     },
18127                     
18128                     inputblock
18129                     
18130                 ];
18131
18132         } else {
18133             
18134                 Roo.log(" no label && no align");
18135                 cfg.cn = [  inputblock ] ;
18136                 
18137                 
18138         }
18139         if(this.boxLabel){
18140              var boxLabelCfg = {
18141                 tag: 'label',
18142                 //'for': id, // box label is handled by onclick - so no for...
18143                 cls: 'box-label',
18144                 html: this.boxLabel
18145             }
18146             
18147             if(this.tooltip){
18148                 boxLabelCfg.tooltip = this.tooltip;
18149             }
18150              
18151             cfg.cn.push(boxLabelCfg);
18152         }
18153         
18154         
18155        
18156         return cfg;
18157         
18158     },
18159     
18160     /**
18161      * return the real input element.
18162      */
18163     inputEl: function ()
18164     {
18165         return this.el.select('input.roo-' + this.inputType,true).first();
18166     },
18167     
18168     labelEl: function()
18169     {
18170         return this.el.select('label.control-label',true).first();
18171     },
18172     /* depricated... */
18173     
18174     label: function()
18175     {
18176         return this.labelEl();
18177     },
18178     
18179     initEvents : function()
18180     {
18181 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18182         
18183         this.inputEl().on('click', this.onClick,  this);
18184         
18185         if (this.boxLabel) { 
18186             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18187         }
18188         
18189         this.startValue = this.getValue();
18190         
18191         if(this.groupId){
18192             Roo.bootstrap.CheckBox.register(this);
18193         }
18194     },
18195     
18196     onClick : function()
18197     {   
18198         this.setChecked(!this.checked);
18199     },
18200     
18201     setChecked : function(state,suppressEvent)
18202     {
18203         this.startValue = this.getValue();
18204         
18205         if(this.inputType == 'radio'){
18206             
18207             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18208                 e.dom.checked = false;
18209             });
18210             
18211             this.inputEl().dom.checked = true;
18212             
18213             this.inputEl().dom.value = this.inputValue;
18214             
18215             if(suppressEvent !== true){
18216                 this.fireEvent('check', this, true);
18217             }
18218             
18219             this.validate();
18220             
18221             return;
18222         }
18223         
18224         this.checked = state;
18225         
18226         this.inputEl().dom.checked = state;
18227         
18228         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18229         
18230         if(suppressEvent !== true){
18231             this.fireEvent('check', this, state);
18232         }
18233         
18234         this.validate();
18235     },
18236     
18237     getValue : function()
18238     {
18239         if(this.inputType == 'radio'){
18240             return this.getGroupValue();
18241         }
18242         
18243         return this.inputEl().getValue();
18244         
18245     },
18246     
18247     getGroupValue : function()
18248     {
18249         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18250             return '';
18251         }
18252         
18253         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18254     },
18255     
18256     setValue : function(v,suppressEvent)
18257     {
18258         if(this.inputType == 'radio'){
18259             this.setGroupValue(v, suppressEvent);
18260             return;
18261         }
18262         
18263         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18264         
18265         this.validate();
18266     },
18267     
18268     setGroupValue : function(v, suppressEvent)
18269     {
18270         this.startValue = this.getValue();
18271         
18272         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18273             e.dom.checked = false;
18274             
18275             if(e.dom.value == v){
18276                 e.dom.checked = true;
18277             }
18278         });
18279         
18280         if(suppressEvent !== true){
18281             this.fireEvent('check', this, true);
18282         }
18283
18284         this.validate();
18285         
18286         return;
18287     },
18288     
18289     validate : function()
18290     {
18291         if(
18292                 this.disabled || 
18293                 (this.inputType == 'radio' && this.validateRadio()) ||
18294                 (this.inputType == 'checkbox' && this.validateCheckbox())
18295         ){
18296             this.markValid();
18297             return true;
18298         }
18299         
18300         this.markInvalid();
18301         return false;
18302     },
18303     
18304     validateRadio : function()
18305     {
18306         var valid = false;
18307         
18308         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18309             if(!e.dom.checked){
18310                 return;
18311             }
18312             
18313             valid = true;
18314             
18315             return false;
18316         });
18317         
18318         return valid;
18319     },
18320     
18321     validateCheckbox : function()
18322     {
18323         if(!this.groupId){
18324             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18325         }
18326         
18327         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18328         
18329         if(!group){
18330             return false;
18331         }
18332         
18333         var r = false;
18334         
18335         for(var i in group){
18336             if(r){
18337                 break;
18338             }
18339             
18340             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18341         }
18342         
18343         return r;
18344     },
18345     
18346     /**
18347      * Mark this field as valid
18348      */
18349     markValid : function()
18350     {
18351         if(this.allowBlank){
18352             return;
18353         }
18354         
18355         var _this = this;
18356         
18357         this.fireEvent('valid', this);
18358         
18359         if(this.inputType == 'radio'){
18360             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18361                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18362                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18363             });
18364             
18365             return;
18366         }
18367         
18368         if(!this.groupId){
18369             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18370             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18371             return;
18372         }
18373         
18374         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18375             
18376         if(!group){
18377             return;
18378         }
18379         
18380         for(var i in group){
18381             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18382             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18383         }
18384     },
18385     
18386      /**
18387      * Mark this field as invalid
18388      * @param {String} msg The validation message
18389      */
18390     markInvalid : function(msg)
18391     {
18392         if(this.allowBlank){
18393             return;
18394         }
18395         
18396         var _this = this;
18397         
18398         this.fireEvent('invalid', this, msg);
18399         
18400         if(this.inputType == 'radio'){
18401             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18402                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18403                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18404             });
18405             
18406             return;
18407         }
18408         
18409         if(!this.groupId){
18410             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18411             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18412             return;
18413         }
18414         
18415         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18416             
18417         if(!group){
18418             return;
18419         }
18420         
18421         for(var i in group){
18422             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18423             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18424         }
18425         
18426     }
18427     
18428 });
18429
18430 Roo.apply(Roo.bootstrap.CheckBox, {
18431     
18432     groups: {},
18433     
18434      /**
18435     * register a CheckBox Group
18436     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18437     */
18438     register : function(checkbox)
18439     {
18440         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18441             this.groups[checkbox.groupId] = {};
18442         }
18443         
18444         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18445             return;
18446         }
18447         
18448         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18449         
18450     },
18451     /**
18452     * fetch a CheckBox Group based on the group ID
18453     * @param {string} the group ID
18454     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18455     */
18456     get: function(groupId) {
18457         if (typeof(this.groups[groupId]) == 'undefined') {
18458             return false;
18459         }
18460         
18461         return this.groups[groupId] ;
18462     }
18463     
18464     
18465 });
18466 /*
18467  * - LGPL
18468  *
18469  * Radio
18470  *
18471  *
18472  * not inline
18473  *<div class="radio">
18474   <label>
18475     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18476     Option one is this and that&mdash;be sure to include why it's great
18477   </label>
18478 </div>
18479  *
18480  *
18481  *inline
18482  *<span>
18483  *<label class="radio-inline">fieldLabel</label>
18484  *<label class="radio-inline">
18485   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18486 </label>
18487 <span>
18488  * 
18489  * 
18490  */
18491
18492 /**
18493  * @class Roo.bootstrap.Radio
18494  * @extends Roo.bootstrap.CheckBox
18495  * Bootstrap Radio class
18496
18497  * @constructor
18498  * Create a new Radio
18499  * @param {Object} config The config object
18500  */
18501
18502 Roo.bootstrap.Radio = function(config){
18503     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18504    
18505 };
18506
18507 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18508     
18509     inputType: 'radio',
18510     inputValue: '',
18511     valueOff: '',
18512     
18513     getAutoCreate : function()
18514     {
18515         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18516         align = align || 'left'; // default...
18517         
18518         
18519         
18520         var id = Roo.id();
18521         
18522         var cfg = {
18523                 tag : this.inline ? 'span' : 'div',
18524                 cls : '',
18525                 cn : []
18526         };
18527         
18528         var inline = this.inline ? ' radio-inline' : '';
18529         
18530         var lbl = {
18531                 tag: 'label' ,
18532                 // does not need for, as we wrap the input with it..
18533                 'for' : id,
18534                 cls : 'control-label box-label' + inline,
18535                 cn : []
18536         };
18537         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18538         
18539         var fieldLabel = {
18540             tag: 'label' ,
18541             //cls : 'control-label' + inline,
18542             html : this.fieldLabel,
18543             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18544         };
18545         
18546  
18547         
18548         
18549         var input =  {
18550             tag: 'input',
18551             id : id,
18552             type : this.inputType,
18553             //value : (!this.checked) ? this.valueOff : this.inputValue,
18554             value : this.inputValue,
18555             cls : 'roo-radio',
18556             placeholder : this.placeholder || '' // ?? needed????
18557             
18558         };
18559         if (this.weight) { // Validity check?
18560             input.cls += " radio-" + this.weight;
18561         }
18562         if (this.disabled) {
18563             input.disabled=true;
18564         }
18565         
18566         if(this.checked){
18567             input.checked = this.checked;
18568         }
18569         
18570         if (this.name) {
18571             input.name = this.name;
18572         }
18573         
18574         if (this.size) {
18575             input.cls += ' input-' + this.size;
18576         }
18577         
18578         //?? can span's inline have a width??
18579         
18580         var settings=this;
18581         ['xs','sm','md','lg'].map(function(size){
18582             if (settings[size]) {
18583                 cfg.cls += ' col-' + size + '-' + settings[size];
18584             }
18585         });
18586         
18587         var inputblock = input;
18588         
18589         if (this.before || this.after) {
18590             
18591             inputblock = {
18592                 cls : 'input-group',
18593                 tag : 'span',
18594                 cn :  [] 
18595             };
18596             if (this.before) {
18597                 inputblock.cn.push({
18598                     tag :'span',
18599                     cls : 'input-group-addon',
18600                     html : this.before
18601                 });
18602             }
18603             inputblock.cn.push(input);
18604             if (this.after) {
18605                 inputblock.cn.push({
18606                     tag :'span',
18607                     cls : 'input-group-addon',
18608                     html : this.after
18609                 });
18610             }
18611             
18612         };
18613         
18614         
18615         if (this.fieldLabel && this.fieldLabel.length) {
18616             cfg.cn.push(fieldLabel);
18617         }
18618        
18619         // normal bootstrap puts the input inside the label.
18620         // however with our styled version - it has to go after the input.
18621        
18622         //lbl.cn.push(inputblock);
18623         
18624         var lblwrap =  {
18625             tag: 'span',
18626             cls: 'radio' + inline,
18627             cn: [
18628                 inputblock,
18629                 lbl
18630             ]
18631         };
18632         
18633         cfg.cn.push( lblwrap);
18634         
18635         if(this.boxLabel){
18636             lbl.cn.push({
18637                 tag: 'span',
18638                 html: this.boxLabel
18639             })
18640         }
18641          
18642         
18643         return cfg;
18644         
18645     },
18646     
18647     initEvents : function()
18648     {
18649 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18650         
18651         this.inputEl().on('click', this.onClick,  this);
18652         if (this.boxLabel) {
18653             Roo.log('find label')
18654             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18655         }
18656         
18657     },
18658     
18659     inputEl: function ()
18660     {
18661         return this.el.select('input.roo-radio',true).first();
18662     },
18663     onClick : function()
18664     {   
18665         Roo.log("click");
18666         this.setChecked(true);
18667     },
18668     
18669     setChecked : function(state,suppressEvent)
18670     {
18671         if(state){
18672             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18673                 v.dom.checked = false;
18674             });
18675         }
18676         Roo.log(this.inputEl().dom);
18677         this.checked = state;
18678         this.inputEl().dom.checked = state;
18679         
18680         if(suppressEvent !== true){
18681             this.fireEvent('check', this, state);
18682         }
18683         
18684         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18685         
18686     },
18687     
18688     getGroupValue : function()
18689     {
18690         var value = '';
18691         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18692             if(v.dom.checked == true){
18693                 value = v.dom.value;
18694             }
18695         });
18696         
18697         return value;
18698     },
18699     
18700     /**
18701      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18702      * @return {Mixed} value The field value
18703      */
18704     getValue : function(){
18705         return this.getGroupValue();
18706     }
18707     
18708 });
18709
18710  
18711 //<script type="text/javascript">
18712
18713 /*
18714  * Based  Ext JS Library 1.1.1
18715  * Copyright(c) 2006-2007, Ext JS, LLC.
18716  * LGPL
18717  *
18718  */
18719  
18720 /**
18721  * @class Roo.HtmlEditorCore
18722  * @extends Roo.Component
18723  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18724  *
18725  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18726  */
18727
18728 Roo.HtmlEditorCore = function(config){
18729     
18730     
18731     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18732     
18733     
18734     this.addEvents({
18735         /**
18736          * @event initialize
18737          * Fires when the editor is fully initialized (including the iframe)
18738          * @param {Roo.HtmlEditorCore} this
18739          */
18740         initialize: true,
18741         /**
18742          * @event activate
18743          * Fires when the editor is first receives the focus. Any insertion must wait
18744          * until after this event.
18745          * @param {Roo.HtmlEditorCore} this
18746          */
18747         activate: true,
18748          /**
18749          * @event beforesync
18750          * Fires before the textarea is updated with content from the editor iframe. Return false
18751          * to cancel the sync.
18752          * @param {Roo.HtmlEditorCore} this
18753          * @param {String} html
18754          */
18755         beforesync: true,
18756          /**
18757          * @event beforepush
18758          * Fires before the iframe editor is updated with content from the textarea. Return false
18759          * to cancel the push.
18760          * @param {Roo.HtmlEditorCore} this
18761          * @param {String} html
18762          */
18763         beforepush: true,
18764          /**
18765          * @event sync
18766          * Fires when the textarea is updated with content from the editor iframe.
18767          * @param {Roo.HtmlEditorCore} this
18768          * @param {String} html
18769          */
18770         sync: true,
18771          /**
18772          * @event push
18773          * Fires when the iframe editor is updated with content from the textarea.
18774          * @param {Roo.HtmlEditorCore} this
18775          * @param {String} html
18776          */
18777         push: true,
18778         
18779         /**
18780          * @event editorevent
18781          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18782          * @param {Roo.HtmlEditorCore} this
18783          */
18784         editorevent: true
18785         
18786     });
18787     
18788     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18789     
18790     // defaults : white / black...
18791     this.applyBlacklists();
18792     
18793     
18794     
18795 };
18796
18797
18798 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18799
18800
18801      /**
18802      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18803      */
18804     
18805     owner : false,
18806     
18807      /**
18808      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18809      *                        Roo.resizable.
18810      */
18811     resizable : false,
18812      /**
18813      * @cfg {Number} height (in pixels)
18814      */   
18815     height: 300,
18816    /**
18817      * @cfg {Number} width (in pixels)
18818      */   
18819     width: 500,
18820     
18821     /**
18822      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18823      * 
18824      */
18825     stylesheets: false,
18826     
18827     // id of frame..
18828     frameId: false,
18829     
18830     // private properties
18831     validationEvent : false,
18832     deferHeight: true,
18833     initialized : false,
18834     activated : false,
18835     sourceEditMode : false,
18836     onFocus : Roo.emptyFn,
18837     iframePad:3,
18838     hideMode:'offsets',
18839     
18840     clearUp: true,
18841     
18842     // blacklist + whitelisted elements..
18843     black: false,
18844     white: false,
18845      
18846     
18847
18848     /**
18849      * Protected method that will not generally be called directly. It
18850      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18851      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18852      */
18853     getDocMarkup : function(){
18854         // body styles..
18855         var st = '';
18856         
18857         // inherit styels from page...?? 
18858         if (this.stylesheets === false) {
18859             
18860             Roo.get(document.head).select('style').each(function(node) {
18861                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18862             });
18863             
18864             Roo.get(document.head).select('link').each(function(node) { 
18865                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18866             });
18867             
18868         } else if (!this.stylesheets.length) {
18869                 // simple..
18870                 st = '<style type="text/css">' +
18871                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18872                    '</style>';
18873         } else { 
18874             
18875         }
18876         
18877         st +=  '<style type="text/css">' +
18878             'IMG { cursor: pointer } ' +
18879         '</style>';
18880
18881         
18882         return '<html><head>' + st  +
18883             //<style type="text/css">' +
18884             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18885             //'</style>' +
18886             ' </head><body class="roo-htmleditor-body"></body></html>';
18887     },
18888
18889     // private
18890     onRender : function(ct, position)
18891     {
18892         var _t = this;
18893         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18894         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18895         
18896         
18897         this.el.dom.style.border = '0 none';
18898         this.el.dom.setAttribute('tabIndex', -1);
18899         this.el.addClass('x-hidden hide');
18900         
18901         
18902         
18903         if(Roo.isIE){ // fix IE 1px bogus margin
18904             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18905         }
18906        
18907         
18908         this.frameId = Roo.id();
18909         
18910          
18911         
18912         var iframe = this.owner.wrap.createChild({
18913             tag: 'iframe',
18914             cls: 'form-control', // bootstrap..
18915             id: this.frameId,
18916             name: this.frameId,
18917             frameBorder : 'no',
18918             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18919         }, this.el
18920         );
18921         
18922         
18923         this.iframe = iframe.dom;
18924
18925          this.assignDocWin();
18926         
18927         this.doc.designMode = 'on';
18928        
18929         this.doc.open();
18930         this.doc.write(this.getDocMarkup());
18931         this.doc.close();
18932
18933         
18934         var task = { // must defer to wait for browser to be ready
18935             run : function(){
18936                 //console.log("run task?" + this.doc.readyState);
18937                 this.assignDocWin();
18938                 if(this.doc.body || this.doc.readyState == 'complete'){
18939                     try {
18940                         this.doc.designMode="on";
18941                     } catch (e) {
18942                         return;
18943                     }
18944                     Roo.TaskMgr.stop(task);
18945                     this.initEditor.defer(10, this);
18946                 }
18947             },
18948             interval : 10,
18949             duration: 10000,
18950             scope: this
18951         };
18952         Roo.TaskMgr.start(task);
18953
18954     },
18955
18956     // private
18957     onResize : function(w, h)
18958     {
18959          Roo.log('resize: ' +w + ',' + h );
18960         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18961         if(!this.iframe){
18962             return;
18963         }
18964         if(typeof w == 'number'){
18965             
18966             this.iframe.style.width = w + 'px';
18967         }
18968         if(typeof h == 'number'){
18969             
18970             this.iframe.style.height = h + 'px';
18971             if(this.doc){
18972                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18973             }
18974         }
18975         
18976     },
18977
18978     /**
18979      * Toggles the editor between standard and source edit mode.
18980      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18981      */
18982     toggleSourceEdit : function(sourceEditMode){
18983         
18984         this.sourceEditMode = sourceEditMode === true;
18985         
18986         if(this.sourceEditMode){
18987  
18988             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18989             
18990         }else{
18991             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18992             //this.iframe.className = '';
18993             this.deferFocus();
18994         }
18995         //this.setSize(this.owner.wrap.getSize());
18996         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18997     },
18998
18999     
19000   
19001
19002     /**
19003      * Protected method that will not generally be called directly. If you need/want
19004      * custom HTML cleanup, this is the method you should override.
19005      * @param {String} html The HTML to be cleaned
19006      * return {String} The cleaned HTML
19007      */
19008     cleanHtml : function(html){
19009         html = String(html);
19010         if(html.length > 5){
19011             if(Roo.isSafari){ // strip safari nonsense
19012                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19013             }
19014         }
19015         if(html == '&nbsp;'){
19016             html = '';
19017         }
19018         return html;
19019     },
19020
19021     /**
19022      * HTML Editor -> Textarea
19023      * Protected method that will not generally be called directly. Syncs the contents
19024      * of the editor iframe with the textarea.
19025      */
19026     syncValue : function(){
19027         if(this.initialized){
19028             var bd = (this.doc.body || this.doc.documentElement);
19029             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19030             var html = bd.innerHTML;
19031             if(Roo.isSafari){
19032                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19033                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19034                 if(m && m[1]){
19035                     html = '<div style="'+m[0]+'">' + html + '</div>';
19036                 }
19037             }
19038             html = this.cleanHtml(html);
19039             // fix up the special chars.. normaly like back quotes in word...
19040             // however we do not want to do this with chinese..
19041             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19042                 var cc = b.charCodeAt();
19043                 if (
19044                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19045                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19046                     (cc >= 0xf900 && cc < 0xfb00 )
19047                 ) {
19048                         return b;
19049                 }
19050                 return "&#"+cc+";" 
19051             });
19052             if(this.owner.fireEvent('beforesync', this, html) !== false){
19053                 this.el.dom.value = html;
19054                 this.owner.fireEvent('sync', this, html);
19055             }
19056         }
19057     },
19058
19059     /**
19060      * Protected method that will not generally be called directly. Pushes the value of the textarea
19061      * into the iframe editor.
19062      */
19063     pushValue : function(){
19064         if(this.initialized){
19065             var v = this.el.dom.value.trim();
19066             
19067 //            if(v.length < 1){
19068 //                v = '&#160;';
19069 //            }
19070             
19071             if(this.owner.fireEvent('beforepush', this, v) !== false){
19072                 var d = (this.doc.body || this.doc.documentElement);
19073                 d.innerHTML = v;
19074                 this.cleanUpPaste();
19075                 this.el.dom.value = d.innerHTML;
19076                 this.owner.fireEvent('push', this, v);
19077             }
19078         }
19079     },
19080
19081     // private
19082     deferFocus : function(){
19083         this.focus.defer(10, this);
19084     },
19085
19086     // doc'ed in Field
19087     focus : function(){
19088         if(this.win && !this.sourceEditMode){
19089             this.win.focus();
19090         }else{
19091             this.el.focus();
19092         }
19093     },
19094     
19095     assignDocWin: function()
19096     {
19097         var iframe = this.iframe;
19098         
19099          if(Roo.isIE){
19100             this.doc = iframe.contentWindow.document;
19101             this.win = iframe.contentWindow;
19102         } else {
19103 //            if (!Roo.get(this.frameId)) {
19104 //                return;
19105 //            }
19106 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19107 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19108             
19109             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19110                 return;
19111             }
19112             
19113             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19114             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19115         }
19116     },
19117     
19118     // private
19119     initEditor : function(){
19120         //console.log("INIT EDITOR");
19121         this.assignDocWin();
19122         
19123         
19124         
19125         this.doc.designMode="on";
19126         this.doc.open();
19127         this.doc.write(this.getDocMarkup());
19128         this.doc.close();
19129         
19130         var dbody = (this.doc.body || this.doc.documentElement);
19131         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19132         // this copies styles from the containing element into thsi one..
19133         // not sure why we need all of this..
19134         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19135         
19136         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19137         //ss['background-attachment'] = 'fixed'; // w3c
19138         dbody.bgProperties = 'fixed'; // ie
19139         //Roo.DomHelper.applyStyles(dbody, ss);
19140         Roo.EventManager.on(this.doc, {
19141             //'mousedown': this.onEditorEvent,
19142             'mouseup': this.onEditorEvent,
19143             'dblclick': this.onEditorEvent,
19144             'click': this.onEditorEvent,
19145             'keyup': this.onEditorEvent,
19146             buffer:100,
19147             scope: this
19148         });
19149         if(Roo.isGecko){
19150             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19151         }
19152         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19153             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19154         }
19155         this.initialized = true;
19156
19157         this.owner.fireEvent('initialize', this);
19158         this.pushValue();
19159     },
19160
19161     // private
19162     onDestroy : function(){
19163         
19164         
19165         
19166         if(this.rendered){
19167             
19168             //for (var i =0; i < this.toolbars.length;i++) {
19169             //    // fixme - ask toolbars for heights?
19170             //    this.toolbars[i].onDestroy();
19171            // }
19172             
19173             //this.wrap.dom.innerHTML = '';
19174             //this.wrap.remove();
19175         }
19176     },
19177
19178     // private
19179     onFirstFocus : function(){
19180         
19181         this.assignDocWin();
19182         
19183         
19184         this.activated = true;
19185          
19186     
19187         if(Roo.isGecko){ // prevent silly gecko errors
19188             this.win.focus();
19189             var s = this.win.getSelection();
19190             if(!s.focusNode || s.focusNode.nodeType != 3){
19191                 var r = s.getRangeAt(0);
19192                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19193                 r.collapse(true);
19194                 this.deferFocus();
19195             }
19196             try{
19197                 this.execCmd('useCSS', true);
19198                 this.execCmd('styleWithCSS', false);
19199             }catch(e){}
19200         }
19201         this.owner.fireEvent('activate', this);
19202     },
19203
19204     // private
19205     adjustFont: function(btn){
19206         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19207         //if(Roo.isSafari){ // safari
19208         //    adjust *= 2;
19209        // }
19210         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19211         if(Roo.isSafari){ // safari
19212             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19213             v =  (v < 10) ? 10 : v;
19214             v =  (v > 48) ? 48 : v;
19215             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19216             
19217         }
19218         
19219         
19220         v = Math.max(1, v+adjust);
19221         
19222         this.execCmd('FontSize', v  );
19223     },
19224
19225     onEditorEvent : function(e)
19226     {
19227         this.owner.fireEvent('editorevent', this, e);
19228       //  this.updateToolbar();
19229         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19230     },
19231
19232     insertTag : function(tg)
19233     {
19234         // could be a bit smarter... -> wrap the current selected tRoo..
19235         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19236             
19237             range = this.createRange(this.getSelection());
19238             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19239             wrappingNode.appendChild(range.extractContents());
19240             range.insertNode(wrappingNode);
19241
19242             return;
19243             
19244             
19245             
19246         }
19247         this.execCmd("formatblock",   tg);
19248         
19249     },
19250     
19251     insertText : function(txt)
19252     {
19253         
19254         
19255         var range = this.createRange();
19256         range.deleteContents();
19257                //alert(Sender.getAttribute('label'));
19258                
19259         range.insertNode(this.doc.createTextNode(txt));
19260     } ,
19261     
19262      
19263
19264     /**
19265      * Executes a Midas editor command on the editor document and performs necessary focus and
19266      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19267      * @param {String} cmd The Midas command
19268      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19269      */
19270     relayCmd : function(cmd, value){
19271         this.win.focus();
19272         this.execCmd(cmd, value);
19273         this.owner.fireEvent('editorevent', this);
19274         //this.updateToolbar();
19275         this.owner.deferFocus();
19276     },
19277
19278     /**
19279      * Executes a Midas editor command directly on the editor document.
19280      * For visual commands, you should use {@link #relayCmd} instead.
19281      * <b>This should only be called after the editor is initialized.</b>
19282      * @param {String} cmd The Midas command
19283      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19284      */
19285     execCmd : function(cmd, value){
19286         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19287         this.syncValue();
19288     },
19289  
19290  
19291    
19292     /**
19293      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19294      * to insert tRoo.
19295      * @param {String} text | dom node.. 
19296      */
19297     insertAtCursor : function(text)
19298     {
19299         
19300         
19301         
19302         if(!this.activated){
19303             return;
19304         }
19305         /*
19306         if(Roo.isIE){
19307             this.win.focus();
19308             var r = this.doc.selection.createRange();
19309             if(r){
19310                 r.collapse(true);
19311                 r.pasteHTML(text);
19312                 this.syncValue();
19313                 this.deferFocus();
19314             
19315             }
19316             return;
19317         }
19318         */
19319         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19320             this.win.focus();
19321             
19322             
19323             // from jquery ui (MIT licenced)
19324             var range, node;
19325             var win = this.win;
19326             
19327             if (win.getSelection && win.getSelection().getRangeAt) {
19328                 range = win.getSelection().getRangeAt(0);
19329                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19330                 range.insertNode(node);
19331             } else if (win.document.selection && win.document.selection.createRange) {
19332                 // no firefox support
19333                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19334                 win.document.selection.createRange().pasteHTML(txt);
19335             } else {
19336                 // no firefox support
19337                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19338                 this.execCmd('InsertHTML', txt);
19339             } 
19340             
19341             this.syncValue();
19342             
19343             this.deferFocus();
19344         }
19345     },
19346  // private
19347     mozKeyPress : function(e){
19348         if(e.ctrlKey){
19349             var c = e.getCharCode(), cmd;
19350           
19351             if(c > 0){
19352                 c = String.fromCharCode(c).toLowerCase();
19353                 switch(c){
19354                     case 'b':
19355                         cmd = 'bold';
19356                         break;
19357                     case 'i':
19358                         cmd = 'italic';
19359                         break;
19360                     
19361                     case 'u':
19362                         cmd = 'underline';
19363                         break;
19364                     
19365                     case 'v':
19366                         this.cleanUpPaste.defer(100, this);
19367                         return;
19368                         
19369                 }
19370                 if(cmd){
19371                     this.win.focus();
19372                     this.execCmd(cmd);
19373                     this.deferFocus();
19374                     e.preventDefault();
19375                 }
19376                 
19377             }
19378         }
19379     },
19380
19381     // private
19382     fixKeys : function(){ // load time branching for fastest keydown performance
19383         if(Roo.isIE){
19384             return function(e){
19385                 var k = e.getKey(), r;
19386                 if(k == e.TAB){
19387                     e.stopEvent();
19388                     r = this.doc.selection.createRange();
19389                     if(r){
19390                         r.collapse(true);
19391                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19392                         this.deferFocus();
19393                     }
19394                     return;
19395                 }
19396                 
19397                 if(k == e.ENTER){
19398                     r = this.doc.selection.createRange();
19399                     if(r){
19400                         var target = r.parentElement();
19401                         if(!target || target.tagName.toLowerCase() != 'li'){
19402                             e.stopEvent();
19403                             r.pasteHTML('<br />');
19404                             r.collapse(false);
19405                             r.select();
19406                         }
19407                     }
19408                 }
19409                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19410                     this.cleanUpPaste.defer(100, this);
19411                     return;
19412                 }
19413                 
19414                 
19415             };
19416         }else if(Roo.isOpera){
19417             return function(e){
19418                 var k = e.getKey();
19419                 if(k == e.TAB){
19420                     e.stopEvent();
19421                     this.win.focus();
19422                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19423                     this.deferFocus();
19424                 }
19425                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19426                     this.cleanUpPaste.defer(100, this);
19427                     return;
19428                 }
19429                 
19430             };
19431         }else if(Roo.isSafari){
19432             return function(e){
19433                 var k = e.getKey();
19434                 
19435                 if(k == e.TAB){
19436                     e.stopEvent();
19437                     this.execCmd('InsertText','\t');
19438                     this.deferFocus();
19439                     return;
19440                 }
19441                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19442                     this.cleanUpPaste.defer(100, this);
19443                     return;
19444                 }
19445                 
19446              };
19447         }
19448     }(),
19449     
19450     getAllAncestors: function()
19451     {
19452         var p = this.getSelectedNode();
19453         var a = [];
19454         if (!p) {
19455             a.push(p); // push blank onto stack..
19456             p = this.getParentElement();
19457         }
19458         
19459         
19460         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19461             a.push(p);
19462             p = p.parentNode;
19463         }
19464         a.push(this.doc.body);
19465         return a;
19466     },
19467     lastSel : false,
19468     lastSelNode : false,
19469     
19470     
19471     getSelection : function() 
19472     {
19473         this.assignDocWin();
19474         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19475     },
19476     
19477     getSelectedNode: function() 
19478     {
19479         // this may only work on Gecko!!!
19480         
19481         // should we cache this!!!!
19482         
19483         
19484         
19485          
19486         var range = this.createRange(this.getSelection()).cloneRange();
19487         
19488         if (Roo.isIE) {
19489             var parent = range.parentElement();
19490             while (true) {
19491                 var testRange = range.duplicate();
19492                 testRange.moveToElementText(parent);
19493                 if (testRange.inRange(range)) {
19494                     break;
19495                 }
19496                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19497                     break;
19498                 }
19499                 parent = parent.parentElement;
19500             }
19501             return parent;
19502         }
19503         
19504         // is ancestor a text element.
19505         var ac =  range.commonAncestorContainer;
19506         if (ac.nodeType == 3) {
19507             ac = ac.parentNode;
19508         }
19509         
19510         var ar = ac.childNodes;
19511          
19512         var nodes = [];
19513         var other_nodes = [];
19514         var has_other_nodes = false;
19515         for (var i=0;i<ar.length;i++) {
19516             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19517                 continue;
19518             }
19519             // fullly contained node.
19520             
19521             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19522                 nodes.push(ar[i]);
19523                 continue;
19524             }
19525             
19526             // probably selected..
19527             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19528                 other_nodes.push(ar[i]);
19529                 continue;
19530             }
19531             // outer..
19532             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19533                 continue;
19534             }
19535             
19536             
19537             has_other_nodes = true;
19538         }
19539         if (!nodes.length && other_nodes.length) {
19540             nodes= other_nodes;
19541         }
19542         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19543             return false;
19544         }
19545         
19546         return nodes[0];
19547     },
19548     createRange: function(sel)
19549     {
19550         // this has strange effects when using with 
19551         // top toolbar - not sure if it's a great idea.
19552         //this.editor.contentWindow.focus();
19553         if (typeof sel != "undefined") {
19554             try {
19555                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19556             } catch(e) {
19557                 return this.doc.createRange();
19558             }
19559         } else {
19560             return this.doc.createRange();
19561         }
19562     },
19563     getParentElement: function()
19564     {
19565         
19566         this.assignDocWin();
19567         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19568         
19569         var range = this.createRange(sel);
19570          
19571         try {
19572             var p = range.commonAncestorContainer;
19573             while (p.nodeType == 3) { // text node
19574                 p = p.parentNode;
19575             }
19576             return p;
19577         } catch (e) {
19578             return null;
19579         }
19580     
19581     },
19582     /***
19583      *
19584      * Range intersection.. the hard stuff...
19585      *  '-1' = before
19586      *  '0' = hits..
19587      *  '1' = after.
19588      *         [ -- selected range --- ]
19589      *   [fail]                        [fail]
19590      *
19591      *    basically..
19592      *      if end is before start or  hits it. fail.
19593      *      if start is after end or hits it fail.
19594      *
19595      *   if either hits (but other is outside. - then it's not 
19596      *   
19597      *    
19598      **/
19599     
19600     
19601     // @see http://www.thismuchiknow.co.uk/?p=64.
19602     rangeIntersectsNode : function(range, node)
19603     {
19604         var nodeRange = node.ownerDocument.createRange();
19605         try {
19606             nodeRange.selectNode(node);
19607         } catch (e) {
19608             nodeRange.selectNodeContents(node);
19609         }
19610     
19611         var rangeStartRange = range.cloneRange();
19612         rangeStartRange.collapse(true);
19613     
19614         var rangeEndRange = range.cloneRange();
19615         rangeEndRange.collapse(false);
19616     
19617         var nodeStartRange = nodeRange.cloneRange();
19618         nodeStartRange.collapse(true);
19619     
19620         var nodeEndRange = nodeRange.cloneRange();
19621         nodeEndRange.collapse(false);
19622     
19623         return rangeStartRange.compareBoundaryPoints(
19624                  Range.START_TO_START, nodeEndRange) == -1 &&
19625                rangeEndRange.compareBoundaryPoints(
19626                  Range.START_TO_START, nodeStartRange) == 1;
19627         
19628          
19629     },
19630     rangeCompareNode : function(range, node)
19631     {
19632         var nodeRange = node.ownerDocument.createRange();
19633         try {
19634             nodeRange.selectNode(node);
19635         } catch (e) {
19636             nodeRange.selectNodeContents(node);
19637         }
19638         
19639         
19640         range.collapse(true);
19641     
19642         nodeRange.collapse(true);
19643      
19644         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19645         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19646          
19647         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19648         
19649         var nodeIsBefore   =  ss == 1;
19650         var nodeIsAfter    = ee == -1;
19651         
19652         if (nodeIsBefore && nodeIsAfter)
19653             return 0; // outer
19654         if (!nodeIsBefore && nodeIsAfter)
19655             return 1; //right trailed.
19656         
19657         if (nodeIsBefore && !nodeIsAfter)
19658             return 2;  // left trailed.
19659         // fully contined.
19660         return 3;
19661     },
19662
19663     // private? - in a new class?
19664     cleanUpPaste :  function()
19665     {
19666         // cleans up the whole document..
19667         Roo.log('cleanuppaste');
19668         
19669         this.cleanUpChildren(this.doc.body);
19670         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19671         if (clean != this.doc.body.innerHTML) {
19672             this.doc.body.innerHTML = clean;
19673         }
19674         
19675     },
19676     
19677     cleanWordChars : function(input) {// change the chars to hex code
19678         var he = Roo.HtmlEditorCore;
19679         
19680         var output = input;
19681         Roo.each(he.swapCodes, function(sw) { 
19682             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19683             
19684             output = output.replace(swapper, sw[1]);
19685         });
19686         
19687         return output;
19688     },
19689     
19690     
19691     cleanUpChildren : function (n)
19692     {
19693         if (!n.childNodes.length) {
19694             return;
19695         }
19696         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19697            this.cleanUpChild(n.childNodes[i]);
19698         }
19699     },
19700     
19701     
19702         
19703     
19704     cleanUpChild : function (node)
19705     {
19706         var ed = this;
19707         //console.log(node);
19708         if (node.nodeName == "#text") {
19709             // clean up silly Windows -- stuff?
19710             return; 
19711         }
19712         if (node.nodeName == "#comment") {
19713             node.parentNode.removeChild(node);
19714             // clean up silly Windows -- stuff?
19715             return; 
19716         }
19717         var lcname = node.tagName.toLowerCase();
19718         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19719         // whitelist of tags..
19720         
19721         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19722             // remove node.
19723             node.parentNode.removeChild(node);
19724             return;
19725             
19726         }
19727         
19728         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19729         
19730         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19731         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19732         
19733         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19734         //    remove_keep_children = true;
19735         //}
19736         
19737         if (remove_keep_children) {
19738             this.cleanUpChildren(node);
19739             // inserts everything just before this node...
19740             while (node.childNodes.length) {
19741                 var cn = node.childNodes[0];
19742                 node.removeChild(cn);
19743                 node.parentNode.insertBefore(cn, node);
19744             }
19745             node.parentNode.removeChild(node);
19746             return;
19747         }
19748         
19749         if (!node.attributes || !node.attributes.length) {
19750             this.cleanUpChildren(node);
19751             return;
19752         }
19753         
19754         function cleanAttr(n,v)
19755         {
19756             
19757             if (v.match(/^\./) || v.match(/^\//)) {
19758                 return;
19759             }
19760             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19761                 return;
19762             }
19763             if (v.match(/^#/)) {
19764                 return;
19765             }
19766 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19767             node.removeAttribute(n);
19768             
19769         }
19770         
19771         var cwhite = this.cwhite;
19772         var cblack = this.cblack;
19773             
19774         function cleanStyle(n,v)
19775         {
19776             if (v.match(/expression/)) { //XSS?? should we even bother..
19777                 node.removeAttribute(n);
19778                 return;
19779             }
19780             
19781             var parts = v.split(/;/);
19782             var clean = [];
19783             
19784             Roo.each(parts, function(p) {
19785                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19786                 if (!p.length) {
19787                     return true;
19788                 }
19789                 var l = p.split(':').shift().replace(/\s+/g,'');
19790                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19791                 
19792                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19793 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19794                     //node.removeAttribute(n);
19795                     return true;
19796                 }
19797                 //Roo.log()
19798                 // only allow 'c whitelisted system attributes'
19799                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19800 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19801                     //node.removeAttribute(n);
19802                     return true;
19803                 }
19804                 
19805                 
19806                  
19807                 
19808                 clean.push(p);
19809                 return true;
19810             });
19811             if (clean.length) { 
19812                 node.setAttribute(n, clean.join(';'));
19813             } else {
19814                 node.removeAttribute(n);
19815             }
19816             
19817         }
19818         
19819         
19820         for (var i = node.attributes.length-1; i > -1 ; i--) {
19821             var a = node.attributes[i];
19822             //console.log(a);
19823             
19824             if (a.name.toLowerCase().substr(0,2)=='on')  {
19825                 node.removeAttribute(a.name);
19826                 continue;
19827             }
19828             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19829                 node.removeAttribute(a.name);
19830                 continue;
19831             }
19832             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19833                 cleanAttr(a.name,a.value); // fixme..
19834                 continue;
19835             }
19836             if (a.name == 'style') {
19837                 cleanStyle(a.name,a.value);
19838                 continue;
19839             }
19840             /// clean up MS crap..
19841             // tecnically this should be a list of valid class'es..
19842             
19843             
19844             if (a.name == 'class') {
19845                 if (a.value.match(/^Mso/)) {
19846                     node.className = '';
19847                 }
19848                 
19849                 if (a.value.match(/body/)) {
19850                     node.className = '';
19851                 }
19852                 continue;
19853             }
19854             
19855             // style cleanup!?
19856             // class cleanup?
19857             
19858         }
19859         
19860         
19861         this.cleanUpChildren(node);
19862         
19863         
19864     },
19865     
19866     /**
19867      * Clean up MS wordisms...
19868      */
19869     cleanWord : function(node)
19870     {
19871         
19872         
19873         if (!node) {
19874             this.cleanWord(this.doc.body);
19875             return;
19876         }
19877         if (node.nodeName == "#text") {
19878             // clean up silly Windows -- stuff?
19879             return; 
19880         }
19881         if (node.nodeName == "#comment") {
19882             node.parentNode.removeChild(node);
19883             // clean up silly Windows -- stuff?
19884             return; 
19885         }
19886         
19887         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19888             node.parentNode.removeChild(node);
19889             return;
19890         }
19891         
19892         // remove - but keep children..
19893         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19894             while (node.childNodes.length) {
19895                 var cn = node.childNodes[0];
19896                 node.removeChild(cn);
19897                 node.parentNode.insertBefore(cn, node);
19898             }
19899             node.parentNode.removeChild(node);
19900             this.iterateChildren(node, this.cleanWord);
19901             return;
19902         }
19903         // clean styles
19904         if (node.className.length) {
19905             
19906             var cn = node.className.split(/\W+/);
19907             var cna = [];
19908             Roo.each(cn, function(cls) {
19909                 if (cls.match(/Mso[a-zA-Z]+/)) {
19910                     return;
19911                 }
19912                 cna.push(cls);
19913             });
19914             node.className = cna.length ? cna.join(' ') : '';
19915             if (!cna.length) {
19916                 node.removeAttribute("class");
19917             }
19918         }
19919         
19920         if (node.hasAttribute("lang")) {
19921             node.removeAttribute("lang");
19922         }
19923         
19924         if (node.hasAttribute("style")) {
19925             
19926             var styles = node.getAttribute("style").split(";");
19927             var nstyle = [];
19928             Roo.each(styles, function(s) {
19929                 if (!s.match(/:/)) {
19930                     return;
19931                 }
19932                 var kv = s.split(":");
19933                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19934                     return;
19935                 }
19936                 // what ever is left... we allow.
19937                 nstyle.push(s);
19938             });
19939             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19940             if (!nstyle.length) {
19941                 node.removeAttribute('style');
19942             }
19943         }
19944         this.iterateChildren(node, this.cleanWord);
19945         
19946         
19947         
19948     },
19949     /**
19950      * iterateChildren of a Node, calling fn each time, using this as the scole..
19951      * @param {DomNode} node node to iterate children of.
19952      * @param {Function} fn method of this class to call on each item.
19953      */
19954     iterateChildren : function(node, fn)
19955     {
19956         if (!node.childNodes.length) {
19957                 return;
19958         }
19959         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19960            fn.call(this, node.childNodes[i])
19961         }
19962     },
19963     
19964     
19965     /**
19966      * cleanTableWidths.
19967      *
19968      * Quite often pasting from word etc.. results in tables with column and widths.
19969      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19970      *
19971      */
19972     cleanTableWidths : function(node)
19973     {
19974          
19975          
19976         if (!node) {
19977             this.cleanTableWidths(this.doc.body);
19978             return;
19979         }
19980         
19981         // ignore list...
19982         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19983             return; 
19984         }
19985         Roo.log(node.tagName);
19986         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19987             this.iterateChildren(node, this.cleanTableWidths);
19988             return;
19989         }
19990         if (node.hasAttribute('width')) {
19991             node.removeAttribute('width');
19992         }
19993         
19994          
19995         if (node.hasAttribute("style")) {
19996             // pretty basic...
19997             
19998             var styles = node.getAttribute("style").split(";");
19999             var nstyle = [];
20000             Roo.each(styles, function(s) {
20001                 if (!s.match(/:/)) {
20002                     return;
20003                 }
20004                 var kv = s.split(":");
20005                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20006                     return;
20007                 }
20008                 // what ever is left... we allow.
20009                 nstyle.push(s);
20010             });
20011             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20012             if (!nstyle.length) {
20013                 node.removeAttribute('style');
20014             }
20015         }
20016         
20017         this.iterateChildren(node, this.cleanTableWidths);
20018         
20019         
20020     },
20021     
20022     
20023     
20024     
20025     domToHTML : function(currentElement, depth, nopadtext) {
20026         
20027         depth = depth || 0;
20028         nopadtext = nopadtext || false;
20029     
20030         if (!currentElement) {
20031             return this.domToHTML(this.doc.body);
20032         }
20033         
20034         //Roo.log(currentElement);
20035         var j;
20036         var allText = false;
20037         var nodeName = currentElement.nodeName;
20038         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20039         
20040         if  (nodeName == '#text') {
20041             
20042             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20043         }
20044         
20045         
20046         var ret = '';
20047         if (nodeName != 'BODY') {
20048              
20049             var i = 0;
20050             // Prints the node tagName, such as <A>, <IMG>, etc
20051             if (tagName) {
20052                 var attr = [];
20053                 for(i = 0; i < currentElement.attributes.length;i++) {
20054                     // quoting?
20055                     var aname = currentElement.attributes.item(i).name;
20056                     if (!currentElement.attributes.item(i).value.length) {
20057                         continue;
20058                     }
20059                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20060                 }
20061                 
20062                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20063             } 
20064             else {
20065                 
20066                 // eack
20067             }
20068         } else {
20069             tagName = false;
20070         }
20071         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20072             return ret;
20073         }
20074         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20075             nopadtext = true;
20076         }
20077         
20078         
20079         // Traverse the tree
20080         i = 0;
20081         var currentElementChild = currentElement.childNodes.item(i);
20082         var allText = true;
20083         var innerHTML  = '';
20084         lastnode = '';
20085         while (currentElementChild) {
20086             // Formatting code (indent the tree so it looks nice on the screen)
20087             var nopad = nopadtext;
20088             if (lastnode == 'SPAN') {
20089                 nopad  = true;
20090             }
20091             // text
20092             if  (currentElementChild.nodeName == '#text') {
20093                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20094                 toadd = nopadtext ? toadd : toadd.trim();
20095                 if (!nopad && toadd.length > 80) {
20096                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20097                 }
20098                 innerHTML  += toadd;
20099                 
20100                 i++;
20101                 currentElementChild = currentElement.childNodes.item(i);
20102                 lastNode = '';
20103                 continue;
20104             }
20105             allText = false;
20106             
20107             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20108                 
20109             // Recursively traverse the tree structure of the child node
20110             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20111             lastnode = currentElementChild.nodeName;
20112             i++;
20113             currentElementChild=currentElement.childNodes.item(i);
20114         }
20115         
20116         ret += innerHTML;
20117         
20118         if (!allText) {
20119                 // The remaining code is mostly for formatting the tree
20120             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20121         }
20122         
20123         
20124         if (tagName) {
20125             ret+= "</"+tagName+">";
20126         }
20127         return ret;
20128         
20129     },
20130         
20131     applyBlacklists : function()
20132     {
20133         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20134         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20135         
20136         this.white = [];
20137         this.black = [];
20138         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20139             if (b.indexOf(tag) > -1) {
20140                 return;
20141             }
20142             this.white.push(tag);
20143             
20144         }, this);
20145         
20146         Roo.each(w, function(tag) {
20147             if (b.indexOf(tag) > -1) {
20148                 return;
20149             }
20150             if (this.white.indexOf(tag) > -1) {
20151                 return;
20152             }
20153             this.white.push(tag);
20154             
20155         }, this);
20156         
20157         
20158         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20159             if (w.indexOf(tag) > -1) {
20160                 return;
20161             }
20162             this.black.push(tag);
20163             
20164         }, this);
20165         
20166         Roo.each(b, function(tag) {
20167             if (w.indexOf(tag) > -1) {
20168                 return;
20169             }
20170             if (this.black.indexOf(tag) > -1) {
20171                 return;
20172             }
20173             this.black.push(tag);
20174             
20175         }, this);
20176         
20177         
20178         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20179         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20180         
20181         this.cwhite = [];
20182         this.cblack = [];
20183         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20184             if (b.indexOf(tag) > -1) {
20185                 return;
20186             }
20187             this.cwhite.push(tag);
20188             
20189         }, this);
20190         
20191         Roo.each(w, function(tag) {
20192             if (b.indexOf(tag) > -1) {
20193                 return;
20194             }
20195             if (this.cwhite.indexOf(tag) > -1) {
20196                 return;
20197             }
20198             this.cwhite.push(tag);
20199             
20200         }, this);
20201         
20202         
20203         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20204             if (w.indexOf(tag) > -1) {
20205                 return;
20206             }
20207             this.cblack.push(tag);
20208             
20209         }, this);
20210         
20211         Roo.each(b, function(tag) {
20212             if (w.indexOf(tag) > -1) {
20213                 return;
20214             }
20215             if (this.cblack.indexOf(tag) > -1) {
20216                 return;
20217             }
20218             this.cblack.push(tag);
20219             
20220         }, this);
20221     },
20222     
20223     setStylesheets : function(stylesheets)
20224     {
20225         if(typeof(stylesheets) == 'string'){
20226             Roo.get(this.iframe.contentDocument.head).createChild({
20227                 tag : 'link',
20228                 rel : 'stylesheet',
20229                 type : 'text/css',
20230                 href : stylesheets
20231             });
20232             
20233             return;
20234         }
20235         var _this = this;
20236      
20237         Roo.each(stylesheets, function(s) {
20238             if(!s.length){
20239                 return;
20240             }
20241             
20242             Roo.get(_this.iframe.contentDocument.head).createChild({
20243                 tag : 'link',
20244                 rel : 'stylesheet',
20245                 type : 'text/css',
20246                 href : s
20247             });
20248         });
20249
20250         
20251     },
20252     
20253     removeStylesheets : function()
20254     {
20255         var _this = this;
20256         
20257         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20258             s.remove();
20259         });
20260     }
20261     
20262     // hide stuff that is not compatible
20263     /**
20264      * @event blur
20265      * @hide
20266      */
20267     /**
20268      * @event change
20269      * @hide
20270      */
20271     /**
20272      * @event focus
20273      * @hide
20274      */
20275     /**
20276      * @event specialkey
20277      * @hide
20278      */
20279     /**
20280      * @cfg {String} fieldClass @hide
20281      */
20282     /**
20283      * @cfg {String} focusClass @hide
20284      */
20285     /**
20286      * @cfg {String} autoCreate @hide
20287      */
20288     /**
20289      * @cfg {String} inputType @hide
20290      */
20291     /**
20292      * @cfg {String} invalidClass @hide
20293      */
20294     /**
20295      * @cfg {String} invalidText @hide
20296      */
20297     /**
20298      * @cfg {String} msgFx @hide
20299      */
20300     /**
20301      * @cfg {String} validateOnBlur @hide
20302      */
20303 });
20304
20305 Roo.HtmlEditorCore.white = [
20306         'area', 'br', 'img', 'input', 'hr', 'wbr',
20307         
20308        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20309        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20310        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20311        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20312        'table',   'ul',         'xmp', 
20313        
20314        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20315       'thead',   'tr', 
20316      
20317       'dir', 'menu', 'ol', 'ul', 'dl',
20318        
20319       'embed',  'object'
20320 ];
20321
20322
20323 Roo.HtmlEditorCore.black = [
20324     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20325         'applet', // 
20326         'base',   'basefont', 'bgsound', 'blink',  'body', 
20327         'frame',  'frameset', 'head',    'html',   'ilayer', 
20328         'iframe', 'layer',  'link',     'meta',    'object',   
20329         'script', 'style' ,'title',  'xml' // clean later..
20330 ];
20331 Roo.HtmlEditorCore.clean = [
20332     'script', 'style', 'title', 'xml'
20333 ];
20334 Roo.HtmlEditorCore.remove = [
20335     'font'
20336 ];
20337 // attributes..
20338
20339 Roo.HtmlEditorCore.ablack = [
20340     'on'
20341 ];
20342     
20343 Roo.HtmlEditorCore.aclean = [ 
20344     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20345 ];
20346
20347 // protocols..
20348 Roo.HtmlEditorCore.pwhite= [
20349         'http',  'https',  'mailto'
20350 ];
20351
20352 // white listed style attributes.
20353 Roo.HtmlEditorCore.cwhite= [
20354       //  'text-align', /// default is to allow most things..
20355       
20356          
20357 //        'font-size'//??
20358 ];
20359
20360 // black listed style attributes.
20361 Roo.HtmlEditorCore.cblack= [
20362       //  'font-size' -- this can be set by the project 
20363 ];
20364
20365
20366 Roo.HtmlEditorCore.swapCodes   =[ 
20367     [    8211, "--" ], 
20368     [    8212, "--" ], 
20369     [    8216,  "'" ],  
20370     [    8217, "'" ],  
20371     [    8220, '"' ],  
20372     [    8221, '"' ],  
20373     [    8226, "*" ],  
20374     [    8230, "..." ]
20375 ]; 
20376
20377     /*
20378  * - LGPL
20379  *
20380  * HtmlEditor
20381  * 
20382  */
20383
20384 /**
20385  * @class Roo.bootstrap.HtmlEditor
20386  * @extends Roo.bootstrap.TextArea
20387  * Bootstrap HtmlEditor class
20388
20389  * @constructor
20390  * Create a new HtmlEditor
20391  * @param {Object} config The config object
20392  */
20393
20394 Roo.bootstrap.HtmlEditor = function(config){
20395     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20396     if (!this.toolbars) {
20397         this.toolbars = [];
20398     }
20399     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20400     this.addEvents({
20401             /**
20402              * @event initialize
20403              * Fires when the editor is fully initialized (including the iframe)
20404              * @param {HtmlEditor} this
20405              */
20406             initialize: true,
20407             /**
20408              * @event activate
20409              * Fires when the editor is first receives the focus. Any insertion must wait
20410              * until after this event.
20411              * @param {HtmlEditor} this
20412              */
20413             activate: true,
20414              /**
20415              * @event beforesync
20416              * Fires before the textarea is updated with content from the editor iframe. Return false
20417              * to cancel the sync.
20418              * @param {HtmlEditor} this
20419              * @param {String} html
20420              */
20421             beforesync: true,
20422              /**
20423              * @event beforepush
20424              * Fires before the iframe editor is updated with content from the textarea. Return false
20425              * to cancel the push.
20426              * @param {HtmlEditor} this
20427              * @param {String} html
20428              */
20429             beforepush: true,
20430              /**
20431              * @event sync
20432              * Fires when the textarea is updated with content from the editor iframe.
20433              * @param {HtmlEditor} this
20434              * @param {String} html
20435              */
20436             sync: true,
20437              /**
20438              * @event push
20439              * Fires when the iframe editor is updated with content from the textarea.
20440              * @param {HtmlEditor} this
20441              * @param {String} html
20442              */
20443             push: true,
20444              /**
20445              * @event editmodechange
20446              * Fires when the editor switches edit modes
20447              * @param {HtmlEditor} this
20448              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20449              */
20450             editmodechange: true,
20451             /**
20452              * @event editorevent
20453              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20454              * @param {HtmlEditor} this
20455              */
20456             editorevent: true,
20457             /**
20458              * @event firstfocus
20459              * Fires when on first focus - needed by toolbars..
20460              * @param {HtmlEditor} this
20461              */
20462             firstfocus: true,
20463             /**
20464              * @event autosave
20465              * Auto save the htmlEditor value as a file into Events
20466              * @param {HtmlEditor} this
20467              */
20468             autosave: true,
20469             /**
20470              * @event savedpreview
20471              * preview the saved version of htmlEditor
20472              * @param {HtmlEditor} this
20473              */
20474             savedpreview: true
20475         });
20476 };
20477
20478
20479 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20480     
20481     
20482       /**
20483      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20484      */
20485     toolbars : false,
20486    
20487      /**
20488      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20489      *                        Roo.resizable.
20490      */
20491     resizable : false,
20492      /**
20493      * @cfg {Number} height (in pixels)
20494      */   
20495     height: 300,
20496    /**
20497      * @cfg {Number} width (in pixels)
20498      */   
20499     width: false,
20500     
20501     /**
20502      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20503      * 
20504      */
20505     stylesheets: false,
20506     
20507     // id of frame..
20508     frameId: false,
20509     
20510     // private properties
20511     validationEvent : false,
20512     deferHeight: true,
20513     initialized : false,
20514     activated : false,
20515     
20516     onFocus : Roo.emptyFn,
20517     iframePad:3,
20518     hideMode:'offsets',
20519     
20520     
20521     tbContainer : false,
20522     
20523     toolbarContainer :function() {
20524         return this.wrap.select('.x-html-editor-tb',true).first();
20525     },
20526
20527     /**
20528      * Protected method that will not generally be called directly. It
20529      * is called when the editor creates its toolbar. Override this method if you need to
20530      * add custom toolbar buttons.
20531      * @param {HtmlEditor} editor
20532      */
20533     createToolbar : function(){
20534         
20535         Roo.log("create toolbars");
20536         
20537         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20538         this.toolbars[0].render(this.toolbarContainer());
20539         
20540         return;
20541         
20542 //        if (!editor.toolbars || !editor.toolbars.length) {
20543 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20544 //        }
20545 //        
20546 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20547 //            editor.toolbars[i] = Roo.factory(
20548 //                    typeof(editor.toolbars[i]) == 'string' ?
20549 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20550 //                Roo.bootstrap.HtmlEditor);
20551 //            editor.toolbars[i].init(editor);
20552 //        }
20553     },
20554
20555      
20556     // private
20557     onRender : function(ct, position)
20558     {
20559        // Roo.log("Call onRender: " + this.xtype);
20560         var _t = this;
20561         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20562       
20563         this.wrap = this.inputEl().wrap({
20564             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20565         });
20566         
20567         this.editorcore.onRender(ct, position);
20568          
20569         if (this.resizable) {
20570             this.resizeEl = new Roo.Resizable(this.wrap, {
20571                 pinned : true,
20572                 wrap: true,
20573                 dynamic : true,
20574                 minHeight : this.height,
20575                 height: this.height,
20576                 handles : this.resizable,
20577                 width: this.width,
20578                 listeners : {
20579                     resize : function(r, w, h) {
20580                         _t.onResize(w,h); // -something
20581                     }
20582                 }
20583             });
20584             
20585         }
20586         this.createToolbar(this);
20587        
20588         
20589         if(!this.width && this.resizable){
20590             this.setSize(this.wrap.getSize());
20591         }
20592         if (this.resizeEl) {
20593             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20594             // should trigger onReize..
20595         }
20596         
20597     },
20598
20599     // private
20600     onResize : function(w, h)
20601     {
20602         Roo.log('resize: ' +w + ',' + h );
20603         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20604         var ew = false;
20605         var eh = false;
20606         
20607         if(this.inputEl() ){
20608             if(typeof w == 'number'){
20609                 var aw = w - this.wrap.getFrameWidth('lr');
20610                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20611                 ew = aw;
20612             }
20613             if(typeof h == 'number'){
20614                  var tbh = -11;  // fixme it needs to tool bar size!
20615                 for (var i =0; i < this.toolbars.length;i++) {
20616                     // fixme - ask toolbars for heights?
20617                     tbh += this.toolbars[i].el.getHeight();
20618                     //if (this.toolbars[i].footer) {
20619                     //    tbh += this.toolbars[i].footer.el.getHeight();
20620                     //}
20621                 }
20622               
20623                 
20624                 
20625                 
20626                 
20627                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20628                 ah -= 5; // knock a few pixes off for look..
20629                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20630                 var eh = ah;
20631             }
20632         }
20633         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20634         this.editorcore.onResize(ew,eh);
20635         
20636     },
20637
20638     /**
20639      * Toggles the editor between standard and source edit mode.
20640      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20641      */
20642     toggleSourceEdit : function(sourceEditMode)
20643     {
20644         this.editorcore.toggleSourceEdit(sourceEditMode);
20645         
20646         if(this.editorcore.sourceEditMode){
20647             Roo.log('editor - showing textarea');
20648             
20649 //            Roo.log('in');
20650 //            Roo.log(this.syncValue());
20651             this.syncValue();
20652             this.inputEl().removeClass(['hide', 'x-hidden']);
20653             this.inputEl().dom.removeAttribute('tabIndex');
20654             this.inputEl().focus();
20655         }else{
20656             Roo.log('editor - hiding textarea');
20657 //            Roo.log('out')
20658 //            Roo.log(this.pushValue()); 
20659             this.pushValue();
20660             
20661             this.inputEl().addClass(['hide', 'x-hidden']);
20662             this.inputEl().dom.setAttribute('tabIndex', -1);
20663             //this.deferFocus();
20664         }
20665          
20666         if(this.resizable){
20667             this.setSize(this.wrap.getSize());
20668         }
20669         
20670         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20671     },
20672  
20673     // private (for BoxComponent)
20674     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20675
20676     // private (for BoxComponent)
20677     getResizeEl : function(){
20678         return this.wrap;
20679     },
20680
20681     // private (for BoxComponent)
20682     getPositionEl : function(){
20683         return this.wrap;
20684     },
20685
20686     // private
20687     initEvents : function(){
20688         this.originalValue = this.getValue();
20689     },
20690
20691 //    /**
20692 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20693 //     * @method
20694 //     */
20695 //    markInvalid : Roo.emptyFn,
20696 //    /**
20697 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20698 //     * @method
20699 //     */
20700 //    clearInvalid : Roo.emptyFn,
20701
20702     setValue : function(v){
20703         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20704         this.editorcore.pushValue();
20705     },
20706
20707      
20708     // private
20709     deferFocus : function(){
20710         this.focus.defer(10, this);
20711     },
20712
20713     // doc'ed in Field
20714     focus : function(){
20715         this.editorcore.focus();
20716         
20717     },
20718       
20719
20720     // private
20721     onDestroy : function(){
20722         
20723         
20724         
20725         if(this.rendered){
20726             
20727             for (var i =0; i < this.toolbars.length;i++) {
20728                 // fixme - ask toolbars for heights?
20729                 this.toolbars[i].onDestroy();
20730             }
20731             
20732             this.wrap.dom.innerHTML = '';
20733             this.wrap.remove();
20734         }
20735     },
20736
20737     // private
20738     onFirstFocus : function(){
20739         //Roo.log("onFirstFocus");
20740         this.editorcore.onFirstFocus();
20741          for (var i =0; i < this.toolbars.length;i++) {
20742             this.toolbars[i].onFirstFocus();
20743         }
20744         
20745     },
20746     
20747     // private
20748     syncValue : function()
20749     {   
20750         this.editorcore.syncValue();
20751     },
20752     
20753     pushValue : function()
20754     {   
20755         this.editorcore.pushValue();
20756     }
20757      
20758     
20759     // hide stuff that is not compatible
20760     /**
20761      * @event blur
20762      * @hide
20763      */
20764     /**
20765      * @event change
20766      * @hide
20767      */
20768     /**
20769      * @event focus
20770      * @hide
20771      */
20772     /**
20773      * @event specialkey
20774      * @hide
20775      */
20776     /**
20777      * @cfg {String} fieldClass @hide
20778      */
20779     /**
20780      * @cfg {String} focusClass @hide
20781      */
20782     /**
20783      * @cfg {String} autoCreate @hide
20784      */
20785     /**
20786      * @cfg {String} inputType @hide
20787      */
20788     /**
20789      * @cfg {String} invalidClass @hide
20790      */
20791     /**
20792      * @cfg {String} invalidText @hide
20793      */
20794     /**
20795      * @cfg {String} msgFx @hide
20796      */
20797     /**
20798      * @cfg {String} validateOnBlur @hide
20799      */
20800 });
20801  
20802     
20803    
20804    
20805    
20806       
20807 Roo.namespace('Roo.bootstrap.htmleditor');
20808 /**
20809  * @class Roo.bootstrap.HtmlEditorToolbar1
20810  * Basic Toolbar
20811  * 
20812  * Usage:
20813  *
20814  new Roo.bootstrap.HtmlEditor({
20815     ....
20816     toolbars : [
20817         new Roo.bootstrap.HtmlEditorToolbar1({
20818             disable : { fonts: 1 , format: 1, ..., ... , ...],
20819             btns : [ .... ]
20820         })
20821     }
20822      
20823  * 
20824  * @cfg {Object} disable List of elements to disable..
20825  * @cfg {Array} btns List of additional buttons.
20826  * 
20827  * 
20828  * NEEDS Extra CSS? 
20829  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20830  */
20831  
20832 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20833 {
20834     
20835     Roo.apply(this, config);
20836     
20837     // default disabled, based on 'good practice'..
20838     this.disable = this.disable || {};
20839     Roo.applyIf(this.disable, {
20840         fontSize : true,
20841         colors : true,
20842         specialElements : true
20843     });
20844     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20845     
20846     this.editor = config.editor;
20847     this.editorcore = config.editor.editorcore;
20848     
20849     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20850     
20851     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20852     // dont call parent... till later.
20853 }
20854 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20855      
20856     bar : true,
20857     
20858     editor : false,
20859     editorcore : false,
20860     
20861     
20862     formats : [
20863         "p" ,  
20864         "h1","h2","h3","h4","h5","h6", 
20865         "pre", "code", 
20866         "abbr", "acronym", "address", "cite", "samp", "var",
20867         'div','span'
20868     ],
20869     
20870     onRender : function(ct, position)
20871     {
20872        // Roo.log("Call onRender: " + this.xtype);
20873         
20874        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20875        Roo.log(this.el);
20876        this.el.dom.style.marginBottom = '0';
20877        var _this = this;
20878        var editorcore = this.editorcore;
20879        var editor= this.editor;
20880        
20881        var children = [];
20882        var btn = function(id,cmd , toggle, handler){
20883        
20884             var  event = toggle ? 'toggle' : 'click';
20885        
20886             var a = {
20887                 size : 'sm',
20888                 xtype: 'Button',
20889                 xns: Roo.bootstrap,
20890                 glyphicon : id,
20891                 cmd : id || cmd,
20892                 enableToggle:toggle !== false,
20893                 //html : 'submit'
20894                 pressed : toggle ? false : null,
20895                 listeners : {}
20896             }
20897             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20898                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20899             }
20900             children.push(a);
20901             return a;
20902        }
20903         
20904         var style = {
20905                 xtype: 'Button',
20906                 size : 'sm',
20907                 xns: Roo.bootstrap,
20908                 glyphicon : 'font',
20909                 //html : 'submit'
20910                 menu : {
20911                     xtype: 'Menu',
20912                     xns: Roo.bootstrap,
20913                     items:  []
20914                 }
20915         };
20916         Roo.each(this.formats, function(f) {
20917             style.menu.items.push({
20918                 xtype :'MenuItem',
20919                 xns: Roo.bootstrap,
20920                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20921                 tagname : f,
20922                 listeners : {
20923                     click : function()
20924                     {
20925                         editorcore.insertTag(this.tagname);
20926                         editor.focus();
20927                     }
20928                 }
20929                 
20930             });
20931         });
20932          children.push(style);   
20933             
20934             
20935         btn('bold',false,true);
20936         btn('italic',false,true);
20937         btn('align-left', 'justifyleft',true);
20938         btn('align-center', 'justifycenter',true);
20939         btn('align-right' , 'justifyright',true);
20940         btn('link', false, false, function(btn) {
20941             //Roo.log("create link?");
20942             var url = prompt(this.createLinkText, this.defaultLinkValue);
20943             if(url && url != 'http:/'+'/'){
20944                 this.editorcore.relayCmd('createlink', url);
20945             }
20946         }),
20947         btn('list','insertunorderedlist',true);
20948         btn('pencil', false,true, function(btn){
20949                 Roo.log(this);
20950                 
20951                 this.toggleSourceEdit(btn.pressed);
20952         });
20953         /*
20954         var cog = {
20955                 xtype: 'Button',
20956                 size : 'sm',
20957                 xns: Roo.bootstrap,
20958                 glyphicon : 'cog',
20959                 //html : 'submit'
20960                 menu : {
20961                     xtype: 'Menu',
20962                     xns: Roo.bootstrap,
20963                     items:  []
20964                 }
20965         };
20966         
20967         cog.menu.items.push({
20968             xtype :'MenuItem',
20969             xns: Roo.bootstrap,
20970             html : Clean styles,
20971             tagname : f,
20972             listeners : {
20973                 click : function()
20974                 {
20975                     editorcore.insertTag(this.tagname);
20976                     editor.focus();
20977                 }
20978             }
20979             
20980         });
20981        */
20982         
20983          
20984        this.xtype = 'NavSimplebar';
20985         
20986         for(var i=0;i< children.length;i++) {
20987             
20988             this.buttons.add(this.addxtypeChild(children[i]));
20989             
20990         }
20991         
20992         editor.on('editorevent', this.updateToolbar, this);
20993     },
20994     onBtnClick : function(id)
20995     {
20996        this.editorcore.relayCmd(id);
20997        this.editorcore.focus();
20998     },
20999     
21000     /**
21001      * Protected method that will not generally be called directly. It triggers
21002      * a toolbar update by reading the markup state of the current selection in the editor.
21003      */
21004     updateToolbar: function(){
21005
21006         if(!this.editorcore.activated){
21007             this.editor.onFirstFocus(); // is this neeed?
21008             return;
21009         }
21010
21011         var btns = this.buttons; 
21012         var doc = this.editorcore.doc;
21013         btns.get('bold').setActive(doc.queryCommandState('bold'));
21014         btns.get('italic').setActive(doc.queryCommandState('italic'));
21015         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21016         
21017         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21018         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21019         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21020         
21021         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21022         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21023          /*
21024         
21025         var ans = this.editorcore.getAllAncestors();
21026         if (this.formatCombo) {
21027             
21028             
21029             var store = this.formatCombo.store;
21030             this.formatCombo.setValue("");
21031             for (var i =0; i < ans.length;i++) {
21032                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21033                     // select it..
21034                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21035                     break;
21036                 }
21037             }
21038         }
21039         
21040         
21041         
21042         // hides menus... - so this cant be on a menu...
21043         Roo.bootstrap.MenuMgr.hideAll();
21044         */
21045         Roo.bootstrap.MenuMgr.hideAll();
21046         //this.editorsyncValue();
21047     },
21048     onFirstFocus: function() {
21049         this.buttons.each(function(item){
21050            item.enable();
21051         });
21052     },
21053     toggleSourceEdit : function(sourceEditMode){
21054         
21055           
21056         if(sourceEditMode){
21057             Roo.log("disabling buttons");
21058            this.buttons.each( function(item){
21059                 if(item.cmd != 'pencil'){
21060                     item.disable();
21061                 }
21062             });
21063           
21064         }else{
21065             Roo.log("enabling buttons");
21066             if(this.editorcore.initialized){
21067                 this.buttons.each( function(item){
21068                     item.enable();
21069                 });
21070             }
21071             
21072         }
21073         Roo.log("calling toggole on editor");
21074         // tell the editor that it's been pressed..
21075         this.editor.toggleSourceEdit(sourceEditMode);
21076        
21077     }
21078 });
21079
21080
21081
21082
21083
21084 /**
21085  * @class Roo.bootstrap.Table.AbstractSelectionModel
21086  * @extends Roo.util.Observable
21087  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21088  * implemented by descendant classes.  This class should not be directly instantiated.
21089  * @constructor
21090  */
21091 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21092     this.locked = false;
21093     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21094 };
21095
21096
21097 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21098     /** @ignore Called by the grid automatically. Do not call directly. */
21099     init : function(grid){
21100         this.grid = grid;
21101         this.initEvents();
21102     },
21103
21104     /**
21105      * Locks the selections.
21106      */
21107     lock : function(){
21108         this.locked = true;
21109     },
21110
21111     /**
21112      * Unlocks the selections.
21113      */
21114     unlock : function(){
21115         this.locked = false;
21116     },
21117
21118     /**
21119      * Returns true if the selections are locked.
21120      * @return {Boolean}
21121      */
21122     isLocked : function(){
21123         return this.locked;
21124     }
21125 });
21126 /**
21127  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21128  * @class Roo.bootstrap.Table.RowSelectionModel
21129  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21130  * It supports multiple selections and keyboard selection/navigation. 
21131  * @constructor
21132  * @param {Object} config
21133  */
21134
21135 Roo.bootstrap.Table.RowSelectionModel = function(config){
21136     Roo.apply(this, config);
21137     this.selections = new Roo.util.MixedCollection(false, function(o){
21138         return o.id;
21139     });
21140
21141     this.last = false;
21142     this.lastActive = false;
21143
21144     this.addEvents({
21145         /**
21146              * @event selectionchange
21147              * Fires when the selection changes
21148              * @param {SelectionModel} this
21149              */
21150             "selectionchange" : true,
21151         /**
21152              * @event afterselectionchange
21153              * Fires after the selection changes (eg. by key press or clicking)
21154              * @param {SelectionModel} this
21155              */
21156             "afterselectionchange" : true,
21157         /**
21158              * @event beforerowselect
21159              * Fires when a row is selected being selected, return false to cancel.
21160              * @param {SelectionModel} this
21161              * @param {Number} rowIndex The selected index
21162              * @param {Boolean} keepExisting False if other selections will be cleared
21163              */
21164             "beforerowselect" : true,
21165         /**
21166              * @event rowselect
21167              * Fires when a row is selected.
21168              * @param {SelectionModel} this
21169              * @param {Number} rowIndex The selected index
21170              * @param {Roo.data.Record} r The record
21171              */
21172             "rowselect" : true,
21173         /**
21174              * @event rowdeselect
21175              * Fires when a row is deselected.
21176              * @param {SelectionModel} this
21177              * @param {Number} rowIndex The selected index
21178              */
21179         "rowdeselect" : true
21180     });
21181     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21182     this.locked = false;
21183 };
21184
21185 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21186     /**
21187      * @cfg {Boolean} singleSelect
21188      * True to allow selection of only one row at a time (defaults to false)
21189      */
21190     singleSelect : false,
21191
21192     // private
21193     initEvents : function(){
21194
21195         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21196             this.grid.on("mousedown", this.handleMouseDown, this);
21197         }else{ // allow click to work like normal
21198             this.grid.on("rowclick", this.handleDragableRowClick, this);
21199         }
21200
21201         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21202             "up" : function(e){
21203                 if(!e.shiftKey){
21204                     this.selectPrevious(e.shiftKey);
21205                 }else if(this.last !== false && this.lastActive !== false){
21206                     var last = this.last;
21207                     this.selectRange(this.last,  this.lastActive-1);
21208                     this.grid.getView().focusRow(this.lastActive);
21209                     if(last !== false){
21210                         this.last = last;
21211                     }
21212                 }else{
21213                     this.selectFirstRow();
21214                 }
21215                 this.fireEvent("afterselectionchange", this);
21216             },
21217             "down" : function(e){
21218                 if(!e.shiftKey){
21219                     this.selectNext(e.shiftKey);
21220                 }else if(this.last !== false && this.lastActive !== false){
21221                     var last = this.last;
21222                     this.selectRange(this.last,  this.lastActive+1);
21223                     this.grid.getView().focusRow(this.lastActive);
21224                     if(last !== false){
21225                         this.last = last;
21226                     }
21227                 }else{
21228                     this.selectFirstRow();
21229                 }
21230                 this.fireEvent("afterselectionchange", this);
21231             },
21232             scope: this
21233         });
21234
21235         var view = this.grid.view;
21236         view.on("refresh", this.onRefresh, this);
21237         view.on("rowupdated", this.onRowUpdated, this);
21238         view.on("rowremoved", this.onRemove, this);
21239     },
21240
21241     // private
21242     onRefresh : function(){
21243         var ds = this.grid.dataSource, i, v = this.grid.view;
21244         var s = this.selections;
21245         s.each(function(r){
21246             if((i = ds.indexOfId(r.id)) != -1){
21247                 v.onRowSelect(i);
21248             }else{
21249                 s.remove(r);
21250             }
21251         });
21252     },
21253
21254     // private
21255     onRemove : function(v, index, r){
21256         this.selections.remove(r);
21257     },
21258
21259     // private
21260     onRowUpdated : function(v, index, r){
21261         if(this.isSelected(r)){
21262             v.onRowSelect(index);
21263         }
21264     },
21265
21266     /**
21267      * Select records.
21268      * @param {Array} records The records to select
21269      * @param {Boolean} keepExisting (optional) True to keep existing selections
21270      */
21271     selectRecords : function(records, keepExisting){
21272         if(!keepExisting){
21273             this.clearSelections();
21274         }
21275         var ds = this.grid.dataSource;
21276         for(var i = 0, len = records.length; i < len; i++){
21277             this.selectRow(ds.indexOf(records[i]), true);
21278         }
21279     },
21280
21281     /**
21282      * Gets the number of selected rows.
21283      * @return {Number}
21284      */
21285     getCount : function(){
21286         return this.selections.length;
21287     },
21288
21289     /**
21290      * Selects the first row in the grid.
21291      */
21292     selectFirstRow : function(){
21293         this.selectRow(0);
21294     },
21295
21296     /**
21297      * Select the last row.
21298      * @param {Boolean} keepExisting (optional) True to keep existing selections
21299      */
21300     selectLastRow : function(keepExisting){
21301         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21302     },
21303
21304     /**
21305      * Selects the row immediately following the last selected row.
21306      * @param {Boolean} keepExisting (optional) True to keep existing selections
21307      */
21308     selectNext : function(keepExisting){
21309         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21310             this.selectRow(this.last+1, keepExisting);
21311             this.grid.getView().focusRow(this.last);
21312         }
21313     },
21314
21315     /**
21316      * Selects the row that precedes the last selected row.
21317      * @param {Boolean} keepExisting (optional) True to keep existing selections
21318      */
21319     selectPrevious : function(keepExisting){
21320         if(this.last){
21321             this.selectRow(this.last-1, keepExisting);
21322             this.grid.getView().focusRow(this.last);
21323         }
21324     },
21325
21326     /**
21327      * Returns the selected records
21328      * @return {Array} Array of selected records
21329      */
21330     getSelections : function(){
21331         return [].concat(this.selections.items);
21332     },
21333
21334     /**
21335      * Returns the first selected record.
21336      * @return {Record}
21337      */
21338     getSelected : function(){
21339         return this.selections.itemAt(0);
21340     },
21341
21342
21343     /**
21344      * Clears all selections.
21345      */
21346     clearSelections : function(fast){
21347         if(this.locked) return;
21348         if(fast !== true){
21349             var ds = this.grid.dataSource;
21350             var s = this.selections;
21351             s.each(function(r){
21352                 this.deselectRow(ds.indexOfId(r.id));
21353             }, this);
21354             s.clear();
21355         }else{
21356             this.selections.clear();
21357         }
21358         this.last = false;
21359     },
21360
21361
21362     /**
21363      * Selects all rows.
21364      */
21365     selectAll : function(){
21366         if(this.locked) return;
21367         this.selections.clear();
21368         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21369             this.selectRow(i, true);
21370         }
21371     },
21372
21373     /**
21374      * Returns True if there is a selection.
21375      * @return {Boolean}
21376      */
21377     hasSelection : function(){
21378         return this.selections.length > 0;
21379     },
21380
21381     /**
21382      * Returns True if the specified row is selected.
21383      * @param {Number/Record} record The record or index of the record to check
21384      * @return {Boolean}
21385      */
21386     isSelected : function(index){
21387         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21388         return (r && this.selections.key(r.id) ? true : false);
21389     },
21390
21391     /**
21392      * Returns True if the specified record id is selected.
21393      * @param {String} id The id of record to check
21394      * @return {Boolean}
21395      */
21396     isIdSelected : function(id){
21397         return (this.selections.key(id) ? true : false);
21398     },
21399
21400     // private
21401     handleMouseDown : function(e, t){
21402         var view = this.grid.getView(), rowIndex;
21403         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21404             return;
21405         };
21406         if(e.shiftKey && this.last !== false){
21407             var last = this.last;
21408             this.selectRange(last, rowIndex, e.ctrlKey);
21409             this.last = last; // reset the last
21410             view.focusRow(rowIndex);
21411         }else{
21412             var isSelected = this.isSelected(rowIndex);
21413             if(e.button !== 0 && isSelected){
21414                 view.focusRow(rowIndex);
21415             }else if(e.ctrlKey && isSelected){
21416                 this.deselectRow(rowIndex);
21417             }else if(!isSelected){
21418                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21419                 view.focusRow(rowIndex);
21420             }
21421         }
21422         this.fireEvent("afterselectionchange", this);
21423     },
21424     // private
21425     handleDragableRowClick :  function(grid, rowIndex, e) 
21426     {
21427         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21428             this.selectRow(rowIndex, false);
21429             grid.view.focusRow(rowIndex);
21430              this.fireEvent("afterselectionchange", this);
21431         }
21432     },
21433     
21434     /**
21435      * Selects multiple rows.
21436      * @param {Array} rows Array of the indexes of the row to select
21437      * @param {Boolean} keepExisting (optional) True to keep existing selections
21438      */
21439     selectRows : function(rows, keepExisting){
21440         if(!keepExisting){
21441             this.clearSelections();
21442         }
21443         for(var i = 0, len = rows.length; i < len; i++){
21444             this.selectRow(rows[i], true);
21445         }
21446     },
21447
21448     /**
21449      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21450      * @param {Number} startRow The index of the first row in the range
21451      * @param {Number} endRow The index of the last row in the range
21452      * @param {Boolean} keepExisting (optional) True to retain existing selections
21453      */
21454     selectRange : function(startRow, endRow, keepExisting){
21455         if(this.locked) return;
21456         if(!keepExisting){
21457             this.clearSelections();
21458         }
21459         if(startRow <= endRow){
21460             for(var i = startRow; i <= endRow; i++){
21461                 this.selectRow(i, true);
21462             }
21463         }else{
21464             for(var i = startRow; i >= endRow; i--){
21465                 this.selectRow(i, true);
21466             }
21467         }
21468     },
21469
21470     /**
21471      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21472      * @param {Number} startRow The index of the first row in the range
21473      * @param {Number} endRow The index of the last row in the range
21474      */
21475     deselectRange : function(startRow, endRow, preventViewNotify){
21476         if(this.locked) return;
21477         for(var i = startRow; i <= endRow; i++){
21478             this.deselectRow(i, preventViewNotify);
21479         }
21480     },
21481
21482     /**
21483      * Selects a row.
21484      * @param {Number} row The index of the row to select
21485      * @param {Boolean} keepExisting (optional) True to keep existing selections
21486      */
21487     selectRow : function(index, keepExisting, preventViewNotify){
21488         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21489         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21490             if(!keepExisting || this.singleSelect){
21491                 this.clearSelections();
21492             }
21493             var r = this.grid.dataSource.getAt(index);
21494             this.selections.add(r);
21495             this.last = this.lastActive = index;
21496             if(!preventViewNotify){
21497                 this.grid.getView().onRowSelect(index);
21498             }
21499             this.fireEvent("rowselect", this, index, r);
21500             this.fireEvent("selectionchange", this);
21501         }
21502     },
21503
21504     /**
21505      * Deselects a row.
21506      * @param {Number} row The index of the row to deselect
21507      */
21508     deselectRow : function(index, preventViewNotify){
21509         if(this.locked) return;
21510         if(this.last == index){
21511             this.last = false;
21512         }
21513         if(this.lastActive == index){
21514             this.lastActive = false;
21515         }
21516         var r = this.grid.dataSource.getAt(index);
21517         this.selections.remove(r);
21518         if(!preventViewNotify){
21519             this.grid.getView().onRowDeselect(index);
21520         }
21521         this.fireEvent("rowdeselect", this, index);
21522         this.fireEvent("selectionchange", this);
21523     },
21524
21525     // private
21526     restoreLast : function(){
21527         if(this._last){
21528             this.last = this._last;
21529         }
21530     },
21531
21532     // private
21533     acceptsNav : function(row, col, cm){
21534         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21535     },
21536
21537     // private
21538     onEditorKey : function(field, e){
21539         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21540         if(k == e.TAB){
21541             e.stopEvent();
21542             ed.completeEdit();
21543             if(e.shiftKey){
21544                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21545             }else{
21546                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21547             }
21548         }else if(k == e.ENTER && !e.ctrlKey){
21549             e.stopEvent();
21550             ed.completeEdit();
21551             if(e.shiftKey){
21552                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21553             }else{
21554                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21555             }
21556         }else if(k == e.ESC){
21557             ed.cancelEdit();
21558         }
21559         if(newCell){
21560             g.startEditing(newCell[0], newCell[1]);
21561         }
21562     }
21563 });/*
21564  * Based on:
21565  * Ext JS Library 1.1.1
21566  * Copyright(c) 2006-2007, Ext JS, LLC.
21567  *
21568  * Originally Released Under LGPL - original licence link has changed is not relivant.
21569  *
21570  * Fork - LGPL
21571  * <script type="text/javascript">
21572  */
21573  
21574 /**
21575  * @class Roo.bootstrap.PagingToolbar
21576  * @extends Roo.Row
21577  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21578  * @constructor
21579  * Create a new PagingToolbar
21580  * @param {Object} config The config object
21581  */
21582 Roo.bootstrap.PagingToolbar = function(config)
21583 {
21584     // old args format still supported... - xtype is prefered..
21585         // created from xtype...
21586     var ds = config.dataSource;
21587     this.toolbarItems = [];
21588     if (config.items) {
21589         this.toolbarItems = config.items;
21590 //        config.items = [];
21591     }
21592     
21593     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21594     this.ds = ds;
21595     this.cursor = 0;
21596     if (ds) { 
21597         this.bind(ds);
21598     }
21599     
21600     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21601     
21602 };
21603
21604 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21605     /**
21606      * @cfg {Roo.data.Store} dataSource
21607      * The underlying data store providing the paged data
21608      */
21609     /**
21610      * @cfg {String/HTMLElement/Element} container
21611      * container The id or element that will contain the toolbar
21612      */
21613     /**
21614      * @cfg {Boolean} displayInfo
21615      * True to display the displayMsg (defaults to false)
21616      */
21617     /**
21618      * @cfg {Number} pageSize
21619      * The number of records to display per page (defaults to 20)
21620      */
21621     pageSize: 20,
21622     /**
21623      * @cfg {String} displayMsg
21624      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21625      */
21626     displayMsg : 'Displaying {0} - {1} of {2}',
21627     /**
21628      * @cfg {String} emptyMsg
21629      * The message to display when no records are found (defaults to "No data to display")
21630      */
21631     emptyMsg : 'No data to display',
21632     /**
21633      * Customizable piece of the default paging text (defaults to "Page")
21634      * @type String
21635      */
21636     beforePageText : "Page",
21637     /**
21638      * Customizable piece of the default paging text (defaults to "of %0")
21639      * @type String
21640      */
21641     afterPageText : "of {0}",
21642     /**
21643      * Customizable piece of the default paging text (defaults to "First Page")
21644      * @type String
21645      */
21646     firstText : "First Page",
21647     /**
21648      * Customizable piece of the default paging text (defaults to "Previous Page")
21649      * @type String
21650      */
21651     prevText : "Previous Page",
21652     /**
21653      * Customizable piece of the default paging text (defaults to "Next Page")
21654      * @type String
21655      */
21656     nextText : "Next Page",
21657     /**
21658      * Customizable piece of the default paging text (defaults to "Last Page")
21659      * @type String
21660      */
21661     lastText : "Last Page",
21662     /**
21663      * Customizable piece of the default paging text (defaults to "Refresh")
21664      * @type String
21665      */
21666     refreshText : "Refresh",
21667
21668     buttons : false,
21669     // private
21670     onRender : function(ct, position) 
21671     {
21672         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21673         this.navgroup.parentId = this.id;
21674         this.navgroup.onRender(this.el, null);
21675         // add the buttons to the navgroup
21676         
21677         if(this.displayInfo){
21678             Roo.log(this.el.select('ul.navbar-nav',true).first());
21679             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21680             this.displayEl = this.el.select('.x-paging-info', true).first();
21681 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21682 //            this.displayEl = navel.el.select('span',true).first();
21683         }
21684         
21685         var _this = this;
21686         
21687         if(this.buttons){
21688             Roo.each(_this.buttons, function(e){
21689                Roo.factory(e).onRender(_this.el, null);
21690             });
21691         }
21692             
21693         Roo.each(_this.toolbarItems, function(e) {
21694             _this.navgroup.addItem(e);
21695         });
21696         
21697         
21698         this.first = this.navgroup.addItem({
21699             tooltip: this.firstText,
21700             cls: "prev",
21701             icon : 'fa fa-backward',
21702             disabled: true,
21703             preventDefault: true,
21704             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21705         });
21706         
21707         this.prev =  this.navgroup.addItem({
21708             tooltip: this.prevText,
21709             cls: "prev",
21710             icon : 'fa fa-step-backward',
21711             disabled: true,
21712             preventDefault: true,
21713             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21714         });
21715     //this.addSeparator();
21716         
21717         
21718         var field = this.navgroup.addItem( {
21719             tagtype : 'span',
21720             cls : 'x-paging-position',
21721             
21722             html : this.beforePageText  +
21723                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21724                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21725          } ); //?? escaped?
21726         
21727         this.field = field.el.select('input', true).first();
21728         this.field.on("keydown", this.onPagingKeydown, this);
21729         this.field.on("focus", function(){this.dom.select();});
21730     
21731     
21732         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21733         //this.field.setHeight(18);
21734         //this.addSeparator();
21735         this.next = this.navgroup.addItem({
21736             tooltip: this.nextText,
21737             cls: "next",
21738             html : ' <i class="fa fa-step-forward">',
21739             disabled: true,
21740             preventDefault: true,
21741             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21742         });
21743         this.last = this.navgroup.addItem({
21744             tooltip: this.lastText,
21745             icon : 'fa fa-forward',
21746             cls: "next",
21747             disabled: true,
21748             preventDefault: true,
21749             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21750         });
21751     //this.addSeparator();
21752         this.loading = this.navgroup.addItem({
21753             tooltip: this.refreshText,
21754             icon: 'fa fa-refresh',
21755             preventDefault: true,
21756             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21757         });
21758
21759     },
21760
21761     // private
21762     updateInfo : function(){
21763         if(this.displayEl){
21764             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21765             var msg = count == 0 ?
21766                 this.emptyMsg :
21767                 String.format(
21768                     this.displayMsg,
21769                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21770                 );
21771             this.displayEl.update(msg);
21772         }
21773     },
21774
21775     // private
21776     onLoad : function(ds, r, o){
21777        this.cursor = o.params ? o.params.start : 0;
21778        var d = this.getPageData(),
21779             ap = d.activePage,
21780             ps = d.pages;
21781         
21782        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21783        this.field.dom.value = ap;
21784        this.first.setDisabled(ap == 1);
21785        this.prev.setDisabled(ap == 1);
21786        this.next.setDisabled(ap == ps);
21787        this.last.setDisabled(ap == ps);
21788        this.loading.enable();
21789        this.updateInfo();
21790     },
21791
21792     // private
21793     getPageData : function(){
21794         var total = this.ds.getTotalCount();
21795         return {
21796             total : total,
21797             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21798             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21799         };
21800     },
21801
21802     // private
21803     onLoadError : function(){
21804         this.loading.enable();
21805     },
21806
21807     // private
21808     onPagingKeydown : function(e){
21809         var k = e.getKey();
21810         var d = this.getPageData();
21811         if(k == e.RETURN){
21812             var v = this.field.dom.value, pageNum;
21813             if(!v || isNaN(pageNum = parseInt(v, 10))){
21814                 this.field.dom.value = d.activePage;
21815                 return;
21816             }
21817             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21818             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21819             e.stopEvent();
21820         }
21821         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))
21822         {
21823           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21824           this.field.dom.value = pageNum;
21825           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21826           e.stopEvent();
21827         }
21828         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21829         {
21830           var v = this.field.dom.value, pageNum; 
21831           var increment = (e.shiftKey) ? 10 : 1;
21832           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21833             increment *= -1;
21834           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21835             this.field.dom.value = d.activePage;
21836             return;
21837           }
21838           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21839           {
21840             this.field.dom.value = parseInt(v, 10) + increment;
21841             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21843           }
21844           e.stopEvent();
21845         }
21846     },
21847
21848     // private
21849     beforeLoad : function(){
21850         if(this.loading){
21851             this.loading.disable();
21852         }
21853     },
21854
21855     // private
21856     onClick : function(which){
21857         
21858         var ds = this.ds;
21859         if (!ds) {
21860             return;
21861         }
21862         
21863         switch(which){
21864             case "first":
21865                 ds.load({params:{start: 0, limit: this.pageSize}});
21866             break;
21867             case "prev":
21868                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21869             break;
21870             case "next":
21871                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21872             break;
21873             case "last":
21874                 var total = ds.getTotalCount();
21875                 var extra = total % this.pageSize;
21876                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21877                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21878             break;
21879             case "refresh":
21880                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21881             break;
21882         }
21883     },
21884
21885     /**
21886      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21887      * @param {Roo.data.Store} store The data store to unbind
21888      */
21889     unbind : function(ds){
21890         ds.un("beforeload", this.beforeLoad, this);
21891         ds.un("load", this.onLoad, this);
21892         ds.un("loadexception", this.onLoadError, this);
21893         ds.un("remove", this.updateInfo, this);
21894         ds.un("add", this.updateInfo, this);
21895         this.ds = undefined;
21896     },
21897
21898     /**
21899      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21900      * @param {Roo.data.Store} store The data store to bind
21901      */
21902     bind : function(ds){
21903         ds.on("beforeload", this.beforeLoad, this);
21904         ds.on("load", this.onLoad, this);
21905         ds.on("loadexception", this.onLoadError, this);
21906         ds.on("remove", this.updateInfo, this);
21907         ds.on("add", this.updateInfo, this);
21908         this.ds = ds;
21909     }
21910 });/*
21911  * - LGPL
21912  *
21913  * element
21914  * 
21915  */
21916
21917 /**
21918  * @class Roo.bootstrap.MessageBar
21919  * @extends Roo.bootstrap.Component
21920  * Bootstrap MessageBar class
21921  * @cfg {String} html contents of the MessageBar
21922  * @cfg {String} weight (info | success | warning | danger) default info
21923  * @cfg {String} beforeClass insert the bar before the given class
21924  * @cfg {Boolean} closable (true | false) default false
21925  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21926  * 
21927  * @constructor
21928  * Create a new Element
21929  * @param {Object} config The config object
21930  */
21931
21932 Roo.bootstrap.MessageBar = function(config){
21933     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21934 };
21935
21936 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21937     
21938     html: '',
21939     weight: 'info',
21940     closable: false,
21941     fixed: false,
21942     beforeClass: 'bootstrap-sticky-wrap',
21943     
21944     getAutoCreate : function(){
21945         
21946         var cfg = {
21947             tag: 'div',
21948             cls: 'alert alert-dismissable alert-' + this.weight,
21949             cn: [
21950                 {
21951                     tag: 'span',
21952                     cls: 'message',
21953                     html: this.html || ''
21954                 }
21955             ]
21956         }
21957         
21958         if(this.fixed){
21959             cfg.cls += ' alert-messages-fixed';
21960         }
21961         
21962         if(this.closable){
21963             cfg.cn.push({
21964                 tag: 'button',
21965                 cls: 'close',
21966                 html: 'x'
21967             });
21968         }
21969         
21970         return cfg;
21971     },
21972     
21973     onRender : function(ct, position)
21974     {
21975         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21976         
21977         if(!this.el){
21978             var cfg = Roo.apply({},  this.getAutoCreate());
21979             cfg.id = Roo.id();
21980             
21981             if (this.cls) {
21982                 cfg.cls += ' ' + this.cls;
21983             }
21984             if (this.style) {
21985                 cfg.style = this.style;
21986             }
21987             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21988             
21989             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21990         }
21991         
21992         this.el.select('>button.close').on('click', this.hide, this);
21993         
21994     },
21995     
21996     show : function()
21997     {
21998         if (!this.rendered) {
21999             this.render();
22000         }
22001         
22002         this.el.show();
22003         
22004         this.fireEvent('show', this);
22005         
22006     },
22007     
22008     hide : function()
22009     {
22010         if (!this.rendered) {
22011             this.render();
22012         }
22013         
22014         this.el.hide();
22015         
22016         this.fireEvent('hide', this);
22017     },
22018     
22019     update : function()
22020     {
22021 //        var e = this.el.dom.firstChild;
22022 //        
22023 //        if(this.closable){
22024 //            e = e.nextSibling;
22025 //        }
22026 //        
22027 //        e.data = this.html || '';
22028
22029         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22030     }
22031    
22032 });
22033
22034  
22035
22036      /*
22037  * - LGPL
22038  *
22039  * Graph
22040  * 
22041  */
22042
22043
22044 /**
22045  * @class Roo.bootstrap.Graph
22046  * @extends Roo.bootstrap.Component
22047  * Bootstrap Graph class
22048 > Prameters
22049  -sm {number} sm 4
22050  -md {number} md 5
22051  @cfg {String} graphtype  bar | vbar | pie
22052  @cfg {number} g_x coodinator | centre x (pie)
22053  @cfg {number} g_y coodinator | centre y (pie)
22054  @cfg {number} g_r radius (pie)
22055  @cfg {number} g_height height of the chart (respected by all elements in the set)
22056  @cfg {number} g_width width of the chart (respected by all elements in the set)
22057  @cfg {Object} title The title of the chart
22058     
22059  -{Array}  values
22060  -opts (object) options for the chart 
22061      o {
22062      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22063      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22064      o vgutter (number)
22065      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.
22066      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22067      o to
22068      o stretch (boolean)
22069      o }
22070  -opts (object) options for the pie
22071      o{
22072      o cut
22073      o startAngle (number)
22074      o endAngle (number)
22075      } 
22076  *
22077  * @constructor
22078  * Create a new Input
22079  * @param {Object} config The config object
22080  */
22081
22082 Roo.bootstrap.Graph = function(config){
22083     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22084     
22085     this.addEvents({
22086         // img events
22087         /**
22088          * @event click
22089          * The img click event for the img.
22090          * @param {Roo.EventObject} e
22091          */
22092         "click" : true
22093     });
22094 };
22095
22096 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22097     
22098     sm: 4,
22099     md: 5,
22100     graphtype: 'bar',
22101     g_height: 250,
22102     g_width: 400,
22103     g_x: 50,
22104     g_y: 50,
22105     g_r: 30,
22106     opts:{
22107         //g_colors: this.colors,
22108         g_type: 'soft',
22109         g_gutter: '20%'
22110
22111     },
22112     title : false,
22113
22114     getAutoCreate : function(){
22115         
22116         var cfg = {
22117             tag: 'div',
22118             html : null
22119         }
22120         
22121         
22122         return  cfg;
22123     },
22124
22125     onRender : function(ct,position){
22126         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22127         this.raphael = Raphael(this.el.dom);
22128         
22129                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22130                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22131                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22132                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22133                 /*
22134                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22135                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22136                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22137                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22138                 
22139                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22140                 r.barchart(330, 10, 300, 220, data1);
22141                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22142                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22143                 */
22144                 
22145                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22146                 // r.barchart(30, 30, 560, 250,  xdata, {
22147                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22148                 //     axis : "0 0 1 1",
22149                 //     axisxlabels :  xdata
22150                 //     //yvalues : cols,
22151                    
22152                 // });
22153 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22154 //        
22155 //        this.load(null,xdata,{
22156 //                axis : "0 0 1 1",
22157 //                axisxlabels :  xdata
22158 //                });
22159
22160     },
22161
22162     load : function(graphtype,xdata,opts){
22163         this.raphael.clear();
22164         if(!graphtype) {
22165             graphtype = this.graphtype;
22166         }
22167         if(!opts){
22168             opts = this.opts;
22169         }
22170         var r = this.raphael,
22171             fin = function () {
22172                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22173             },
22174             fout = function () {
22175                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22176             },
22177             pfin = function() {
22178                 this.sector.stop();
22179                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22180
22181                 if (this.label) {
22182                     this.label[0].stop();
22183                     this.label[0].attr({ r: 7.5 });
22184                     this.label[1].attr({ "font-weight": 800 });
22185                 }
22186             },
22187             pfout = function() {
22188                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22189
22190                 if (this.label) {
22191                     this.label[0].animate({ r: 5 }, 500, "bounce");
22192                     this.label[1].attr({ "font-weight": 400 });
22193                 }
22194             };
22195
22196         switch(graphtype){
22197             case 'bar':
22198                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22199                 break;
22200             case 'hbar':
22201                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22202                 break;
22203             case 'pie':
22204 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22205 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22206 //            
22207                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22208                 
22209                 break;
22210
22211         }
22212         
22213         if(this.title){
22214             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22215         }
22216         
22217     },
22218     
22219     setTitle: function(o)
22220     {
22221         this.title = o;
22222     },
22223     
22224     initEvents: function() {
22225         
22226         if(!this.href){
22227             this.el.on('click', this.onClick, this);
22228         }
22229     },
22230     
22231     onClick : function(e)
22232     {
22233         Roo.log('img onclick');
22234         this.fireEvent('click', this, e);
22235     }
22236    
22237 });
22238
22239  
22240 /*
22241  * - LGPL
22242  *
22243  * numberBox
22244  * 
22245  */
22246 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22247
22248 /**
22249  * @class Roo.bootstrap.dash.NumberBox
22250  * @extends Roo.bootstrap.Component
22251  * Bootstrap NumberBox class
22252  * @cfg {String} headline Box headline
22253  * @cfg {String} content Box content
22254  * @cfg {String} icon Box icon
22255  * @cfg {String} footer Footer text
22256  * @cfg {String} fhref Footer href
22257  * 
22258  * @constructor
22259  * Create a new NumberBox
22260  * @param {Object} config The config object
22261  */
22262
22263
22264 Roo.bootstrap.dash.NumberBox = function(config){
22265     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22266     
22267 };
22268
22269 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22270     
22271     headline : '',
22272     content : '',
22273     icon : '',
22274     footer : '',
22275     fhref : '',
22276     ficon : '',
22277     
22278     getAutoCreate : function(){
22279         
22280         var cfg = {
22281             tag : 'div',
22282             cls : 'small-box ',
22283             cn : [
22284                 {
22285                     tag : 'div',
22286                     cls : 'inner',
22287                     cn :[
22288                         {
22289                             tag : 'h3',
22290                             cls : 'roo-headline',
22291                             html : this.headline
22292                         },
22293                         {
22294                             tag : 'p',
22295                             cls : 'roo-content',
22296                             html : this.content
22297                         }
22298                     ]
22299                 }
22300             ]
22301         }
22302         
22303         if(this.icon){
22304             cfg.cn.push({
22305                 tag : 'div',
22306                 cls : 'icon',
22307                 cn :[
22308                     {
22309                         tag : 'i',
22310                         cls : 'ion ' + this.icon
22311                     }
22312                 ]
22313             });
22314         }
22315         
22316         if(this.footer){
22317             var footer = {
22318                 tag : 'a',
22319                 cls : 'small-box-footer',
22320                 href : this.fhref || '#',
22321                 html : this.footer
22322             };
22323             
22324             cfg.cn.push(footer);
22325             
22326         }
22327         
22328         return  cfg;
22329     },
22330
22331     onRender : function(ct,position){
22332         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22333
22334
22335        
22336                 
22337     },
22338
22339     setHeadline: function (value)
22340     {
22341         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22342     },
22343     
22344     setFooter: function (value, href)
22345     {
22346         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22347         
22348         if(href){
22349             this.el.select('a.small-box-footer',true).first().attr('href', href);
22350         }
22351         
22352     },
22353
22354     setContent: function (value)
22355     {
22356         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22357     },
22358
22359     initEvents: function() 
22360     {   
22361         
22362     }
22363     
22364 });
22365
22366  
22367 /*
22368  * - LGPL
22369  *
22370  * TabBox
22371  * 
22372  */
22373 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22374
22375 /**
22376  * @class Roo.bootstrap.dash.TabBox
22377  * @extends Roo.bootstrap.Component
22378  * Bootstrap TabBox class
22379  * @cfg {String} title Title of the TabBox
22380  * @cfg {String} icon Icon of the TabBox
22381  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22382  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22383  * 
22384  * @constructor
22385  * Create a new TabBox
22386  * @param {Object} config The config object
22387  */
22388
22389
22390 Roo.bootstrap.dash.TabBox = function(config){
22391     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22392     this.addEvents({
22393         // raw events
22394         /**
22395          * @event addpane
22396          * When a pane is added
22397          * @param {Roo.bootstrap.dash.TabPane} pane
22398          */
22399         "addpane" : true,
22400         /**
22401          * @event activatepane
22402          * When a pane is activated
22403          * @param {Roo.bootstrap.dash.TabPane} pane
22404          */
22405         "activatepane" : true
22406         
22407          
22408     });
22409     
22410     this.panes = [];
22411 };
22412
22413 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22414
22415     title : '',
22416     icon : false,
22417     showtabs : true,
22418     tabScrollable : false,
22419     
22420     getChildContainer : function()
22421     {
22422         return this.el.select('.tab-content', true).first();
22423     },
22424     
22425     getAutoCreate : function(){
22426         
22427         var header = {
22428             tag: 'li',
22429             cls: 'pull-left header',
22430             html: this.title,
22431             cn : []
22432         };
22433         
22434         if(this.icon){
22435             header.cn.push({
22436                 tag: 'i',
22437                 cls: 'fa ' + this.icon
22438             });
22439         }
22440         
22441         var h = {
22442             tag: 'ul',
22443             cls: 'nav nav-tabs pull-right',
22444             cn: [
22445                 header
22446             ]
22447         };
22448         
22449         if(this.tabScrollable){
22450             h = {
22451                 tag: 'div',
22452                 cls: 'tab-header',
22453                 cn: [
22454                     {
22455                         tag: 'ul',
22456                         cls: 'nav nav-tabs pull-right',
22457                         cn: [
22458                             header
22459                         ]
22460                     }
22461                 ]
22462             }
22463         }
22464         
22465         var cfg = {
22466             tag: 'div',
22467             cls: 'nav-tabs-custom',
22468             cn: [
22469                 h,
22470                 {
22471                     tag: 'div',
22472                     cls: 'tab-content no-padding',
22473                     cn: []
22474                 }
22475             ]
22476         }
22477
22478         return  cfg;
22479     },
22480     initEvents : function()
22481     {
22482         //Roo.log('add add pane handler');
22483         this.on('addpane', this.onAddPane, this);
22484     },
22485      /**
22486      * Updates the box title
22487      * @param {String} html to set the title to.
22488      */
22489     setTitle : function(value)
22490     {
22491         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22492     },
22493     onAddPane : function(pane)
22494     {
22495         this.panes.push(pane);
22496         //Roo.log('addpane');
22497         //Roo.log(pane);
22498         // tabs are rendere left to right..
22499         if(!this.showtabs){
22500             return;
22501         }
22502         
22503         var ctr = this.el.select('.nav-tabs', true).first();
22504          
22505          
22506         var existing = ctr.select('.nav-tab',true);
22507         var qty = existing.getCount();;
22508         
22509         
22510         var tab = ctr.createChild({
22511             tag : 'li',
22512             cls : 'nav-tab' + (qty ? '' : ' active'),
22513             cn : [
22514                 {
22515                     tag : 'a',
22516                     href:'#',
22517                     html : pane.title
22518                 }
22519             ]
22520         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22521         pane.tab = tab;
22522         
22523         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22524         if (!qty) {
22525             pane.el.addClass('active');
22526         }
22527         
22528                 
22529     },
22530     onTabClick : function(ev,un,ob,pane)
22531     {
22532         //Roo.log('tab - prev default');
22533         ev.preventDefault();
22534         
22535         
22536         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22537         pane.tab.addClass('active');
22538         //Roo.log(pane.title);
22539         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22540         // technically we should have a deactivate event.. but maybe add later.
22541         // and it should not de-activate the selected tab...
22542         this.fireEvent('activatepane', pane);
22543         pane.el.addClass('active');
22544         pane.fireEvent('activate');
22545         
22546         
22547     },
22548     
22549     getActivePane : function()
22550     {
22551         var r = false;
22552         Roo.each(this.panes, function(p) {
22553             if(p.el.hasClass('active')){
22554                 r = p;
22555                 return false;
22556             }
22557             
22558             return;
22559         });
22560         
22561         return r;
22562     }
22563     
22564     
22565 });
22566
22567  
22568 /*
22569  * - LGPL
22570  *
22571  * Tab pane
22572  * 
22573  */
22574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22575 /**
22576  * @class Roo.bootstrap.TabPane
22577  * @extends Roo.bootstrap.Component
22578  * Bootstrap TabPane class
22579  * @cfg {Boolean} active (false | true) Default false
22580  * @cfg {String} title title of panel
22581
22582  * 
22583  * @constructor
22584  * Create a new TabPane
22585  * @param {Object} config The config object
22586  */
22587
22588 Roo.bootstrap.dash.TabPane = function(config){
22589     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22590     
22591     this.addEvents({
22592         // raw events
22593         /**
22594          * @event activate
22595          * When a pane is activated
22596          * @param {Roo.bootstrap.dash.TabPane} pane
22597          */
22598         "activate" : true
22599          
22600     });
22601 };
22602
22603 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22604     
22605     active : false,
22606     title : '',
22607     
22608     // the tabBox that this is attached to.
22609     tab : false,
22610      
22611     getAutoCreate : function() 
22612     {
22613         var cfg = {
22614             tag: 'div',
22615             cls: 'tab-pane'
22616         }
22617         
22618         if(this.active){
22619             cfg.cls += ' active';
22620         }
22621         
22622         return cfg;
22623     },
22624     initEvents  : function()
22625     {
22626         //Roo.log('trigger add pane handler');
22627         this.parent().fireEvent('addpane', this)
22628     },
22629     
22630      /**
22631      * Updates the tab title 
22632      * @param {String} html to set the title to.
22633      */
22634     setTitle: function(str)
22635     {
22636         if (!this.tab) {
22637             return;
22638         }
22639         this.title = str;
22640         this.tab.select('a', true).first().dom.innerHTML = str;
22641         
22642     }
22643     
22644     
22645     
22646 });
22647
22648  
22649
22650
22651  /*
22652  * - LGPL
22653  *
22654  * menu
22655  * 
22656  */
22657 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22658
22659 /**
22660  * @class Roo.bootstrap.menu.Menu
22661  * @extends Roo.bootstrap.Component
22662  * Bootstrap Menu class - container for Menu
22663  * @cfg {String} html Text of the menu
22664  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22665  * @cfg {String} icon Font awesome icon
22666  * @cfg {String} pos Menu align to (top | bottom) default bottom
22667  * 
22668  * 
22669  * @constructor
22670  * Create a new Menu
22671  * @param {Object} config The config object
22672  */
22673
22674
22675 Roo.bootstrap.menu.Menu = function(config){
22676     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22677     
22678     this.addEvents({
22679         /**
22680          * @event beforeshow
22681          * Fires before this menu is displayed
22682          * @param {Roo.bootstrap.menu.Menu} this
22683          */
22684         beforeshow : true,
22685         /**
22686          * @event beforehide
22687          * Fires before this menu is hidden
22688          * @param {Roo.bootstrap.menu.Menu} this
22689          */
22690         beforehide : true,
22691         /**
22692          * @event show
22693          * Fires after this menu is displayed
22694          * @param {Roo.bootstrap.menu.Menu} this
22695          */
22696         show : true,
22697         /**
22698          * @event hide
22699          * Fires after this menu is hidden
22700          * @param {Roo.bootstrap.menu.Menu} this
22701          */
22702         hide : true,
22703         /**
22704          * @event click
22705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22706          * @param {Roo.bootstrap.menu.Menu} this
22707          * @param {Roo.EventObject} e
22708          */
22709         click : true
22710     });
22711     
22712 };
22713
22714 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22715     
22716     submenu : false,
22717     html : '',
22718     weight : 'default',
22719     icon : false,
22720     pos : 'bottom',
22721     
22722     
22723     getChildContainer : function() {
22724         if(this.isSubMenu){
22725             return this.el;
22726         }
22727         
22728         return this.el.select('ul.dropdown-menu', true).first();  
22729     },
22730     
22731     getAutoCreate : function()
22732     {
22733         var text = [
22734             {
22735                 tag : 'span',
22736                 cls : 'roo-menu-text',
22737                 html : this.html
22738             }
22739         ];
22740         
22741         if(this.icon){
22742             text.unshift({
22743                 tag : 'i',
22744                 cls : 'fa ' + this.icon
22745             })
22746         }
22747         
22748         
22749         var cfg = {
22750             tag : 'div',
22751             cls : 'btn-group',
22752             cn : [
22753                 {
22754                     tag : 'button',
22755                     cls : 'dropdown-button btn btn-' + this.weight,
22756                     cn : text
22757                 },
22758                 {
22759                     tag : 'button',
22760                     cls : 'dropdown-toggle btn btn-' + this.weight,
22761                     cn : [
22762                         {
22763                             tag : 'span',
22764                             cls : 'caret'
22765                         }
22766                     ]
22767                 },
22768                 {
22769                     tag : 'ul',
22770                     cls : 'dropdown-menu'
22771                 }
22772             ]
22773             
22774         };
22775         
22776         if(this.pos == 'top'){
22777             cfg.cls += ' dropup';
22778         }
22779         
22780         if(this.isSubMenu){
22781             cfg = {
22782                 tag : 'ul',
22783                 cls : 'dropdown-menu'
22784             }
22785         }
22786         
22787         return cfg;
22788     },
22789     
22790     onRender : function(ct, position)
22791     {
22792         this.isSubMenu = ct.hasClass('dropdown-submenu');
22793         
22794         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22795     },
22796     
22797     initEvents : function() 
22798     {
22799         if(this.isSubMenu){
22800             return;
22801         }
22802         
22803         this.hidden = true;
22804         
22805         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22806         this.triggerEl.on('click', this.onTriggerPress, this);
22807         
22808         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22809         this.buttonEl.on('click', this.onClick, this);
22810         
22811     },
22812     
22813     list : function()
22814     {
22815         if(this.isSubMenu){
22816             return this.el;
22817         }
22818         
22819         return this.el.select('ul.dropdown-menu', true).first();
22820     },
22821     
22822     onClick : function(e)
22823     {
22824         this.fireEvent("click", this, e);
22825     },
22826     
22827     onTriggerPress  : function(e)
22828     {   
22829         if (this.isVisible()) {
22830             this.hide();
22831         } else {
22832             this.show();
22833         }
22834     },
22835     
22836     isVisible : function(){
22837         return !this.hidden;
22838     },
22839     
22840     show : function()
22841     {
22842         this.fireEvent("beforeshow", this);
22843         
22844         this.hidden = false;
22845         this.el.addClass('open');
22846         
22847         Roo.get(document).on("mouseup", this.onMouseUp, this);
22848         
22849         this.fireEvent("show", this);
22850         
22851         
22852     },
22853     
22854     hide : function()
22855     {
22856         this.fireEvent("beforehide", this);
22857         
22858         this.hidden = true;
22859         this.el.removeClass('open');
22860         
22861         Roo.get(document).un("mouseup", this.onMouseUp);
22862         
22863         this.fireEvent("hide", this);
22864     },
22865     
22866     onMouseUp : function()
22867     {
22868         this.hide();
22869     }
22870     
22871 });
22872
22873  
22874  /*
22875  * - LGPL
22876  *
22877  * menu item
22878  * 
22879  */
22880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22881
22882 /**
22883  * @class Roo.bootstrap.menu.Item
22884  * @extends Roo.bootstrap.Component
22885  * Bootstrap MenuItem class
22886  * @cfg {Boolean} submenu (true | false) default false
22887  * @cfg {String} html text of the item
22888  * @cfg {String} href the link
22889  * @cfg {Boolean} disable (true | false) default false
22890  * @cfg {Boolean} preventDefault (true | false) default true
22891  * @cfg {String} icon Font awesome icon
22892  * @cfg {String} pos Submenu align to (left | right) default right 
22893  * 
22894  * 
22895  * @constructor
22896  * Create a new Item
22897  * @param {Object} config The config object
22898  */
22899
22900
22901 Roo.bootstrap.menu.Item = function(config){
22902     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22903     this.addEvents({
22904         /**
22905          * @event mouseover
22906          * Fires when the mouse is hovering over this menu
22907          * @param {Roo.bootstrap.menu.Item} this
22908          * @param {Roo.EventObject} e
22909          */
22910         mouseover : true,
22911         /**
22912          * @event mouseout
22913          * Fires when the mouse exits this menu
22914          * @param {Roo.bootstrap.menu.Item} this
22915          * @param {Roo.EventObject} e
22916          */
22917         mouseout : true,
22918         // raw events
22919         /**
22920          * @event click
22921          * The raw click event for the entire grid.
22922          * @param {Roo.EventObject} e
22923          */
22924         click : true
22925     });
22926 };
22927
22928 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22929     
22930     submenu : false,
22931     href : '',
22932     html : '',
22933     preventDefault: true,
22934     disable : false,
22935     icon : false,
22936     pos : 'right',
22937     
22938     getAutoCreate : function()
22939     {
22940         var text = [
22941             {
22942                 tag : 'span',
22943                 cls : 'roo-menu-item-text',
22944                 html : this.html
22945             }
22946         ];
22947         
22948         if(this.icon){
22949             text.unshift({
22950                 tag : 'i',
22951                 cls : 'fa ' + this.icon
22952             })
22953         }
22954         
22955         var cfg = {
22956             tag : 'li',
22957             cn : [
22958                 {
22959                     tag : 'a',
22960                     href : this.href || '#',
22961                     cn : text
22962                 }
22963             ]
22964         };
22965         
22966         if(this.disable){
22967             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22968         }
22969         
22970         if(this.submenu){
22971             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22972             
22973             if(this.pos == 'left'){
22974                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22975             }
22976         }
22977         
22978         return cfg;
22979     },
22980     
22981     initEvents : function() 
22982     {
22983         this.el.on('mouseover', this.onMouseOver, this);
22984         this.el.on('mouseout', this.onMouseOut, this);
22985         
22986         this.el.select('a', true).first().on('click', this.onClick, this);
22987         
22988     },
22989     
22990     onClick : function(e)
22991     {
22992         if(this.preventDefault){
22993             e.preventDefault();
22994         }
22995         
22996         this.fireEvent("click", this, e);
22997     },
22998     
22999     onMouseOver : function(e)
23000     {
23001         if(this.submenu && this.pos == 'left'){
23002             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23003         }
23004         
23005         this.fireEvent("mouseover", this, e);
23006     },
23007     
23008     onMouseOut : function(e)
23009     {
23010         this.fireEvent("mouseout", this, e);
23011     }
23012 });
23013
23014  
23015
23016  /*
23017  * - LGPL
23018  *
23019  * menu separator
23020  * 
23021  */
23022 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23023
23024 /**
23025  * @class Roo.bootstrap.menu.Separator
23026  * @extends Roo.bootstrap.Component
23027  * Bootstrap Separator class
23028  * 
23029  * @constructor
23030  * Create a new Separator
23031  * @param {Object} config The config object
23032  */
23033
23034
23035 Roo.bootstrap.menu.Separator = function(config){
23036     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23037 };
23038
23039 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23040     
23041     getAutoCreate : function(){
23042         var cfg = {
23043             tag : 'li',
23044             cls: 'divider'
23045         };
23046         
23047         return cfg;
23048     }
23049    
23050 });
23051
23052  
23053
23054  /*
23055  * - LGPL
23056  *
23057  * Tooltip
23058  * 
23059  */
23060
23061 /**
23062  * @class Roo.bootstrap.Tooltip
23063  * Bootstrap Tooltip class
23064  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23065  * to determine which dom element triggers the tooltip.
23066  * 
23067  * It needs to add support for additional attributes like tooltip-position
23068  * 
23069  * @constructor
23070  * Create a new Toolti
23071  * @param {Object} config The config object
23072  */
23073
23074 Roo.bootstrap.Tooltip = function(config){
23075     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23076 };
23077
23078 Roo.apply(Roo.bootstrap.Tooltip, {
23079     /**
23080      * @function init initialize tooltip monitoring.
23081      * @static
23082      */
23083     currentEl : false,
23084     currentTip : false,
23085     currentRegion : false,
23086     
23087     //  init : delay?
23088     
23089     init : function()
23090     {
23091         Roo.get(document).on('mouseover', this.enter ,this);
23092         Roo.get(document).on('mouseout', this.leave, this);
23093          
23094         
23095         this.currentTip = new Roo.bootstrap.Tooltip();
23096     },
23097     
23098     enter : function(ev)
23099     {
23100         var dom = ev.getTarget();
23101         
23102         //Roo.log(['enter',dom]);
23103         var el = Roo.fly(dom);
23104         if (this.currentEl) {
23105             //Roo.log(dom);
23106             //Roo.log(this.currentEl);
23107             //Roo.log(this.currentEl.contains(dom));
23108             if (this.currentEl == el) {
23109                 return;
23110             }
23111             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23112                 return;
23113             }
23114
23115         }
23116         
23117         
23118         
23119         if (this.currentTip.el) {
23120             this.currentTip.el.hide(); // force hiding...
23121         }    
23122         //Roo.log(ev);
23123         var bindEl = el;
23124         
23125         // you can not look for children, as if el is the body.. then everythign is the child..
23126         if (!el.attr('tooltip')) { //
23127             if (!el.select("[tooltip]").elements.length) {
23128                 return;
23129             }
23130             // is the mouse over this child...?
23131             bindEl = el.select("[tooltip]").first();
23132             var xy = ev.getXY();
23133             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23134                 //Roo.log("not in region.");
23135                 return;
23136             }
23137             //Roo.log("child element over..");
23138             
23139         }
23140         this.currentEl = bindEl;
23141         this.currentTip.bind(bindEl);
23142         this.currentRegion = Roo.lib.Region.getRegion(dom);
23143         this.currentTip.enter();
23144         
23145     },
23146     leave : function(ev)
23147     {
23148         var dom = ev.getTarget();
23149         //Roo.log(['leave',dom]);
23150         if (!this.currentEl) {
23151             return;
23152         }
23153         
23154         
23155         if (dom != this.currentEl.dom) {
23156             return;
23157         }
23158         var xy = ev.getXY();
23159         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23160             return;
23161         }
23162         // only activate leave if mouse cursor is outside... bounding box..
23163         
23164         
23165         
23166         
23167         if (this.currentTip) {
23168             this.currentTip.leave();
23169         }
23170         //Roo.log('clear currentEl');
23171         this.currentEl = false;
23172         
23173         
23174     },
23175     alignment : {
23176         'left' : ['r-l', [-2,0], 'right'],
23177         'right' : ['l-r', [2,0], 'left'],
23178         'bottom' : ['t-b', [0,2], 'top'],
23179         'top' : [ 'b-t', [0,-2], 'bottom']
23180     }
23181     
23182 });
23183
23184
23185 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23186     
23187     
23188     bindEl : false,
23189     
23190     delay : null, // can be { show : 300 , hide: 500}
23191     
23192     timeout : null,
23193     
23194     hoverState : null, //???
23195     
23196     placement : 'bottom', 
23197     
23198     getAutoCreate : function(){
23199     
23200         var cfg = {
23201            cls : 'tooltip',
23202            role : 'tooltip',
23203            cn : [
23204                 {
23205                     cls : 'tooltip-arrow'
23206                 },
23207                 {
23208                     cls : 'tooltip-inner'
23209                 }
23210            ]
23211         };
23212         
23213         return cfg;
23214     },
23215     bind : function(el)
23216     {
23217         this.bindEl = el;
23218     },
23219       
23220     
23221     enter : function () {
23222        
23223         if (this.timeout != null) {
23224             clearTimeout(this.timeout);
23225         }
23226         
23227         this.hoverState = 'in';
23228          //Roo.log("enter - show");
23229         if (!this.delay || !this.delay.show) {
23230             this.show();
23231             return;
23232         }
23233         var _t = this;
23234         this.timeout = setTimeout(function () {
23235             if (_t.hoverState == 'in') {
23236                 _t.show();
23237             }
23238         }, this.delay.show);
23239     },
23240     leave : function()
23241     {
23242         clearTimeout(this.timeout);
23243     
23244         this.hoverState = 'out';
23245          if (!this.delay || !this.delay.hide) {
23246             this.hide();
23247             return;
23248         }
23249        
23250         var _t = this;
23251         this.timeout = setTimeout(function () {
23252             //Roo.log("leave - timeout");
23253             
23254             if (_t.hoverState == 'out') {
23255                 _t.hide();
23256                 Roo.bootstrap.Tooltip.currentEl = false;
23257             }
23258         }, delay);
23259     },
23260     
23261     show : function ()
23262     {
23263         if (!this.el) {
23264             this.render(document.body);
23265         }
23266         // set content.
23267         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23268         
23269         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23270         
23271         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23272         
23273         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23274         
23275         var placement = typeof this.placement == 'function' ?
23276             this.placement.call(this, this.el, on_el) :
23277             this.placement;
23278             
23279         var autoToken = /\s?auto?\s?/i;
23280         var autoPlace = autoToken.test(placement);
23281         if (autoPlace) {
23282             placement = placement.replace(autoToken, '') || 'top';
23283         }
23284         
23285         //this.el.detach()
23286         //this.el.setXY([0,0]);
23287         this.el.show();
23288         //this.el.dom.style.display='block';
23289         this.el.addClass(placement);
23290         
23291         //this.el.appendTo(on_el);
23292         
23293         var p = this.getPosition();
23294         var box = this.el.getBox();
23295         
23296         if (autoPlace) {
23297             // fixme..
23298         }
23299         var align = Roo.bootstrap.Tooltip.alignment[placement];
23300         this.el.alignTo(this.bindEl, align[0],align[1]);
23301         //var arrow = this.el.select('.arrow',true).first();
23302         //arrow.set(align[2], 
23303         
23304         this.el.addClass('in fade');
23305         this.hoverState = null;
23306         
23307         if (this.el.hasClass('fade')) {
23308             // fade it?
23309         }
23310         
23311     },
23312     hide : function()
23313     {
23314          
23315         if (!this.el) {
23316             return;
23317         }
23318         //this.el.setXY([0,0]);
23319         this.el.removeClass('in');
23320         //this.el.hide();
23321         
23322     }
23323     
23324 });
23325  
23326
23327  /*
23328  * - LGPL
23329  *
23330  * Location Picker
23331  * 
23332  */
23333
23334 /**
23335  * @class Roo.bootstrap.LocationPicker
23336  * @extends Roo.bootstrap.Component
23337  * Bootstrap LocationPicker class
23338  * @cfg {Number} latitude Position when init default 0
23339  * @cfg {Number} longitude Position when init default 0
23340  * @cfg {Number} zoom default 15
23341  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23342  * @cfg {Boolean} mapTypeControl default false
23343  * @cfg {Boolean} disableDoubleClickZoom default false
23344  * @cfg {Boolean} scrollwheel default true
23345  * @cfg {Boolean} streetViewControl default false
23346  * @cfg {Number} radius default 0
23347  * @cfg {String} locationName
23348  * @cfg {Boolean} draggable default true
23349  * @cfg {Boolean} enableAutocomplete default false
23350  * @cfg {Boolean} enableReverseGeocode default true
23351  * @cfg {String} markerTitle
23352  * 
23353  * @constructor
23354  * Create a new LocationPicker
23355  * @param {Object} config The config object
23356  */
23357
23358
23359 Roo.bootstrap.LocationPicker = function(config){
23360     
23361     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23362     
23363     this.addEvents({
23364         /**
23365          * @event initial
23366          * Fires when the picker initialized.
23367          * @param {Roo.bootstrap.LocationPicker} this
23368          * @param {Google Location} location
23369          */
23370         initial : true,
23371         /**
23372          * @event positionchanged
23373          * Fires when the picker position changed.
23374          * @param {Roo.bootstrap.LocationPicker} this
23375          * @param {Google Location} location
23376          */
23377         positionchanged : true,
23378         /**
23379          * @event resize
23380          * Fires when the map resize.
23381          * @param {Roo.bootstrap.LocationPicker} this
23382          */
23383         resize : true,
23384         /**
23385          * @event show
23386          * Fires when the map show.
23387          * @param {Roo.bootstrap.LocationPicker} this
23388          */
23389         show : true,
23390         /**
23391          * @event hide
23392          * Fires when the map hide.
23393          * @param {Roo.bootstrap.LocationPicker} this
23394          */
23395         hide : true,
23396         /**
23397          * @event mapClick
23398          * Fires when click the map.
23399          * @param {Roo.bootstrap.LocationPicker} this
23400          * @param {Map event} e
23401          */
23402         mapClick : true,
23403         /**
23404          * @event mapRightClick
23405          * Fires when right click the map.
23406          * @param {Roo.bootstrap.LocationPicker} this
23407          * @param {Map event} e
23408          */
23409         mapRightClick : true,
23410         /**
23411          * @event markerClick
23412          * Fires when click the marker.
23413          * @param {Roo.bootstrap.LocationPicker} this
23414          * @param {Map event} e
23415          */
23416         markerClick : true,
23417         /**
23418          * @event markerRightClick
23419          * Fires when right click the marker.
23420          * @param {Roo.bootstrap.LocationPicker} this
23421          * @param {Map event} e
23422          */
23423         markerRightClick : true,
23424         /**
23425          * @event OverlayViewDraw
23426          * Fires when OverlayView Draw
23427          * @param {Roo.bootstrap.LocationPicker} this
23428          */
23429         OverlayViewDraw : true,
23430         /**
23431          * @event OverlayViewOnAdd
23432          * Fires when OverlayView Draw
23433          * @param {Roo.bootstrap.LocationPicker} this
23434          */
23435         OverlayViewOnAdd : true,
23436         /**
23437          * @event OverlayViewOnRemove
23438          * Fires when OverlayView Draw
23439          * @param {Roo.bootstrap.LocationPicker} this
23440          */
23441         OverlayViewOnRemove : true,
23442         /**
23443          * @event OverlayViewShow
23444          * Fires when OverlayView Draw
23445          * @param {Roo.bootstrap.LocationPicker} this
23446          * @param {Pixel} cpx
23447          */
23448         OverlayViewShow : true,
23449         /**
23450          * @event OverlayViewHide
23451          * Fires when OverlayView Draw
23452          * @param {Roo.bootstrap.LocationPicker} this
23453          */
23454         OverlayViewHide : true
23455     });
23456         
23457 };
23458
23459 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23460     
23461     gMapContext: false,
23462     
23463     latitude: 0,
23464     longitude: 0,
23465     zoom: 15,
23466     mapTypeId: false,
23467     mapTypeControl: false,
23468     disableDoubleClickZoom: false,
23469     scrollwheel: true,
23470     streetViewControl: false,
23471     radius: 0,
23472     locationName: '',
23473     draggable: true,
23474     enableAutocomplete: false,
23475     enableReverseGeocode: true,
23476     markerTitle: '',
23477     
23478     getAutoCreate: function()
23479     {
23480
23481         var cfg = {
23482             tag: 'div',
23483             cls: 'roo-location-picker'
23484         };
23485         
23486         return cfg
23487     },
23488     
23489     initEvents: function(ct, position)
23490     {       
23491         if(!this.el.getWidth() || this.isApplied()){
23492             return;
23493         }
23494         
23495         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23496         
23497         this.initial();
23498     },
23499     
23500     initial: function()
23501     {
23502         if(!this.mapTypeId){
23503             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23504         }
23505         
23506         this.gMapContext = this.GMapContext();
23507         
23508         this.initOverlayView();
23509         
23510         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23511         
23512         var _this = this;
23513                 
23514         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23515             _this.setPosition(_this.gMapContext.marker.position);
23516         });
23517         
23518         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23519             _this.fireEvent('mapClick', this, event);
23520             
23521         });
23522
23523         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23524             _this.fireEvent('mapRightClick', this, event);
23525             
23526         });
23527         
23528         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23529             _this.fireEvent('markerClick', this, event);
23530             
23531         });
23532
23533         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23534             _this.fireEvent('markerRightClick', this, event);
23535             
23536         });
23537         
23538         this.setPosition(this.gMapContext.location);
23539         
23540         this.fireEvent('initial', this, this.gMapContext.location);
23541     },
23542     
23543     initOverlayView: function()
23544     {
23545         var _this = this;
23546         
23547         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23548             
23549             draw: function()
23550             {
23551                 _this.fireEvent('OverlayViewDraw', _this);
23552             },
23553             
23554             onAdd: function()
23555             {
23556                 _this.fireEvent('OverlayViewOnAdd', _this);
23557             },
23558             
23559             onRemove: function()
23560             {
23561                 _this.fireEvent('OverlayViewOnRemove', _this);
23562             },
23563             
23564             show: function(cpx)
23565             {
23566                 _this.fireEvent('OverlayViewShow', _this, cpx);
23567             },
23568             
23569             hide: function()
23570             {
23571                 _this.fireEvent('OverlayViewHide', _this);
23572             }
23573             
23574         });
23575     },
23576     
23577     fromLatLngToContainerPixel: function(event)
23578     {
23579         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23580     },
23581     
23582     isApplied: function() 
23583     {
23584         return this.getGmapContext() == false ? false : true;
23585     },
23586     
23587     getGmapContext: function() 
23588     {
23589         return this.gMapContext
23590     },
23591     
23592     GMapContext: function() 
23593     {
23594         var position = new google.maps.LatLng(this.latitude, this.longitude);
23595         
23596         var _map = new google.maps.Map(this.el.dom, {
23597             center: position,
23598             zoom: this.zoom,
23599             mapTypeId: this.mapTypeId,
23600             mapTypeControl: this.mapTypeControl,
23601             disableDoubleClickZoom: this.disableDoubleClickZoom,
23602             scrollwheel: this.scrollwheel,
23603             streetViewControl: this.streetViewControl,
23604             locationName: this.locationName,
23605             draggable: this.draggable,
23606             enableAutocomplete: this.enableAutocomplete,
23607             enableReverseGeocode: this.enableReverseGeocode
23608         });
23609         
23610         var _marker = new google.maps.Marker({
23611             position: position,
23612             map: _map,
23613             title: this.markerTitle,
23614             draggable: this.draggable
23615         });
23616         
23617         return {
23618             map: _map,
23619             marker: _marker,
23620             circle: null,
23621             location: position,
23622             radius: this.radius,
23623             locationName: this.locationName,
23624             addressComponents: {
23625                 formatted_address: null,
23626                 addressLine1: null,
23627                 addressLine2: null,
23628                 streetName: null,
23629                 streetNumber: null,
23630                 city: null,
23631                 district: null,
23632                 state: null,
23633                 stateOrProvince: null
23634             },
23635             settings: this,
23636             domContainer: this.el.dom,
23637             geodecoder: new google.maps.Geocoder()
23638         };
23639     },
23640     
23641     drawCircle: function(center, radius, options) 
23642     {
23643         if (this.gMapContext.circle != null) {
23644             this.gMapContext.circle.setMap(null);
23645         }
23646         if (radius > 0) {
23647             radius *= 1;
23648             options = Roo.apply({}, options, {
23649                 strokeColor: "#0000FF",
23650                 strokeOpacity: .35,
23651                 strokeWeight: 2,
23652                 fillColor: "#0000FF",
23653                 fillOpacity: .2
23654             });
23655             
23656             options.map = this.gMapContext.map;
23657             options.radius = radius;
23658             options.center = center;
23659             this.gMapContext.circle = new google.maps.Circle(options);
23660             return this.gMapContext.circle;
23661         }
23662         
23663         return null;
23664     },
23665     
23666     setPosition: function(location) 
23667     {
23668         this.gMapContext.location = location;
23669         this.gMapContext.marker.setPosition(location);
23670         this.gMapContext.map.panTo(location);
23671         this.drawCircle(location, this.gMapContext.radius, {});
23672         
23673         var _this = this;
23674         
23675         if (this.gMapContext.settings.enableReverseGeocode) {
23676             this.gMapContext.geodecoder.geocode({
23677                 latLng: this.gMapContext.location
23678             }, function(results, status) {
23679                 
23680                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23681                     _this.gMapContext.locationName = results[0].formatted_address;
23682                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23683                     
23684                     _this.fireEvent('positionchanged', this, location);
23685                 }
23686             });
23687             
23688             return;
23689         }
23690         
23691         this.fireEvent('positionchanged', this, location);
23692     },
23693     
23694     resize: function()
23695     {
23696         google.maps.event.trigger(this.gMapContext.map, "resize");
23697         
23698         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23699         
23700         this.fireEvent('resize', this);
23701     },
23702     
23703     setPositionByLatLng: function(latitude, longitude)
23704     {
23705         this.setPosition(new google.maps.LatLng(latitude, longitude));
23706     },
23707     
23708     getCurrentPosition: function() 
23709     {
23710         return {
23711             latitude: this.gMapContext.location.lat(),
23712             longitude: this.gMapContext.location.lng()
23713         };
23714     },
23715     
23716     getAddressName: function() 
23717     {
23718         return this.gMapContext.locationName;
23719     },
23720     
23721     getAddressComponents: function() 
23722     {
23723         return this.gMapContext.addressComponents;
23724     },
23725     
23726     address_component_from_google_geocode: function(address_components) 
23727     {
23728         var result = {};
23729         
23730         for (var i = 0; i < address_components.length; i++) {
23731             var component = address_components[i];
23732             if (component.types.indexOf("postal_code") >= 0) {
23733                 result.postalCode = component.short_name;
23734             } else if (component.types.indexOf("street_number") >= 0) {
23735                 result.streetNumber = component.short_name;
23736             } else if (component.types.indexOf("route") >= 0) {
23737                 result.streetName = component.short_name;
23738             } else if (component.types.indexOf("neighborhood") >= 0) {
23739                 result.city = component.short_name;
23740             } else if (component.types.indexOf("locality") >= 0) {
23741                 result.city = component.short_name;
23742             } else if (component.types.indexOf("sublocality") >= 0) {
23743                 result.district = component.short_name;
23744             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23745                 result.stateOrProvince = component.short_name;
23746             } else if (component.types.indexOf("country") >= 0) {
23747                 result.country = component.short_name;
23748             }
23749         }
23750         
23751         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23752         result.addressLine2 = "";
23753         return result;
23754     },
23755     
23756     setZoomLevel: function(zoom)
23757     {
23758         this.gMapContext.map.setZoom(zoom);
23759     },
23760     
23761     show: function()
23762     {
23763         if(!this.el){
23764             return;
23765         }
23766         
23767         this.el.show();
23768         
23769         this.resize();
23770         
23771         this.fireEvent('show', this);
23772     },
23773     
23774     hide: function()
23775     {
23776         if(!this.el){
23777             return;
23778         }
23779         
23780         this.el.hide();
23781         
23782         this.fireEvent('hide', this);
23783     }
23784     
23785 });
23786
23787 Roo.apply(Roo.bootstrap.LocationPicker, {
23788     
23789     OverlayView : function(map, options)
23790     {
23791         options = options || {};
23792         
23793         this.setMap(map);
23794     }
23795     
23796     
23797 });/*
23798  * - LGPL
23799  *
23800  * Alert
23801  * 
23802  */
23803
23804 /**
23805  * @class Roo.bootstrap.Alert
23806  * @extends Roo.bootstrap.Component
23807  * Bootstrap Alert class
23808  * @cfg {String} title The title of alert
23809  * @cfg {String} html The content of alert
23810  * @cfg {String} weight (  success | info | warning | danger )
23811  * @cfg {String} faicon font-awesomeicon
23812  * 
23813  * @constructor
23814  * Create a new alert
23815  * @param {Object} config The config object
23816  */
23817
23818
23819 Roo.bootstrap.Alert = function(config){
23820     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23821     
23822 };
23823
23824 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23825     
23826     title: '',
23827     html: '',
23828     weight: false,
23829     faicon: false,
23830     
23831     getAutoCreate : function()
23832     {
23833         
23834         var cfg = {
23835             tag : 'div',
23836             cls : 'alert',
23837             cn : [
23838                 {
23839                     tag : 'i',
23840                     cls : 'roo-alert-icon'
23841                     
23842                 },
23843                 {
23844                     tag : 'b',
23845                     cls : 'roo-alert-title',
23846                     html : this.title
23847                 },
23848                 {
23849                     tag : 'span',
23850                     cls : 'roo-alert-text',
23851                     html : this.html
23852                 }
23853             ]
23854         };
23855         
23856         if(this.faicon){
23857             cfg.cn[0].cls += ' fa ' + this.faicon;
23858         }
23859         
23860         if(this.weight){
23861             cfg.cls += ' alert-' + this.weight;
23862         }
23863         
23864         return cfg;
23865     },
23866     
23867     initEvents: function() 
23868     {
23869         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23870     },
23871     
23872     setTitle : function(str)
23873     {
23874         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23875     },
23876     
23877     setText : function(str)
23878     {
23879         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23880     },
23881     
23882     setWeight : function(weight)
23883     {
23884         if(this.weight){
23885             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23886         }
23887         
23888         this.weight = weight;
23889         
23890         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23891     },
23892     
23893     setIcon : function(icon)
23894     {
23895         if(this.faicon){
23896             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23897         }
23898         
23899         this.faicon = icon
23900         
23901         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23902     },
23903     
23904     hide: function() 
23905     {
23906         this.el.hide();   
23907     },
23908     
23909     show: function() 
23910     {  
23911         this.el.show();   
23912     }
23913     
23914 });
23915
23916