6d2b355a8eb8469acd6b77cfefe602982ac2654c
[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         var align = this.labelAlign || this.parentLabelAlign();
8531         
8532         var id = Roo.id();
8533         
8534         var cfg = {
8535             cls: 'form-group' //input-group
8536         };
8537         
8538         
8539         var input =  {
8540             tag: 'input',
8541             id : id,
8542             type : this.inputType,
8543             cls : 'form-control',
8544             autocomplete: 'new-password',
8545             placeholder : this.placeholder || '' 
8546             
8547         };
8548         if (this.name) {
8549             input.name = this.name;
8550         }
8551         if (this.size) {
8552             input.cls += ' input-' + this.size;
8553         }
8554         
8555         if (this.disabled) {
8556             input.disabled=true;
8557         }
8558         
8559         var inputblock = input;
8560         
8561         if(this.hasFeedback && !this.allowBlank){
8562             
8563             var feedback = {
8564                 tag: 'span',
8565                 cls: 'glyphicon form-control-feedback'
8566             };
8567             
8568             if(this.removable && !this.editable && !this.tickable){
8569                 inputblock = {
8570                     cls : 'has-feedback',
8571                     cn :  [
8572                         inputblock,
8573                         {
8574                             tag: 'button',
8575                             html : 'x',
8576                             cls : 'roo-combo-removable-btn close'
8577                         },
8578                         feedback
8579                     ] 
8580                 };
8581             } else {
8582                 inputblock = {
8583                     cls : 'has-feedback',
8584                     cn :  [
8585                         inputblock,
8586                         feedback
8587                     ] 
8588                 };
8589             }
8590               
8591         } else {
8592             if(this.removable && !this.editable && !this.tickable){
8593                 inputblock = {
8594                     cls : 'roo-removable',
8595                     cn :  [
8596                         inputblock,
8597                         {
8598                             tag: 'button',
8599                             html : 'x',
8600                             cls : 'roo-combo-removable-btn close'
8601                         }
8602                     ] 
8603                 };
8604             }
8605         }
8606         
8607         if (this.before || this.after) {
8608             
8609             inputblock = {
8610                 cls : 'input-group',
8611                 cn :  [] 
8612             };
8613             if (this.before) {
8614                 inputblock.cn.push({
8615                     tag :'span',
8616                     cls : 'input-group-addon',
8617                     html : this.before
8618                 });
8619             }
8620             
8621             inputblock.cn.push(input);
8622             
8623             if(this.hasFeedback && !this.allowBlank){
8624                 inputblock.cls += ' has-feedback';
8625                 inputblock.cn.push(feedback);
8626             }
8627             
8628             if (this.after) {
8629                 inputblock.cn.push({
8630                     tag :'span',
8631                     cls : 'input-group-addon',
8632                     html : this.after
8633                 });
8634             }
8635             
8636         };
8637         
8638         var box = {
8639             tag: 'div',
8640             cn: [
8641                 {
8642                     tag: 'input',
8643                     type : 'hidden',
8644                     cls: 'form-hidden-field'
8645                 },
8646                 inputblock
8647             ]
8648             
8649         };
8650         
8651         if(this.multiple){
8652             Roo.log('multiple');
8653             
8654             box = {
8655                 tag: 'div',
8656                 cn: [
8657                     {
8658                         tag: 'input',
8659                         type : 'hidden',
8660                         cls: 'form-hidden-field'
8661                     },
8662                     {
8663                         tag: 'ul',
8664                         cls: 'select2-choices',
8665                         cn:[
8666                             {
8667                                 tag: 'li',
8668                                 cls: 'select2-search-field',
8669                                 cn: [
8670
8671                                     inputblock
8672                                 ]
8673                             }
8674                         ]
8675                     }
8676                 ]
8677             }
8678         };
8679         
8680         var combobox = {
8681             cls: 'select2-container input-group',
8682             cn: [
8683                 box
8684 //                {
8685 //                    tag: 'ul',
8686 //                    cls: 'typeahead typeahead-long dropdown-menu',
8687 //                    style: 'display:none'
8688 //                }
8689             ]
8690         };
8691         
8692         if(!this.multiple && this.showToggleBtn){
8693             
8694             var caret = {
8695                         tag: 'span',
8696                         cls: 'caret'
8697              };
8698             if (this.caret != false) {
8699                 caret = {
8700                      tag: 'i',
8701                      cls: 'fa fa-' + this.caret
8702                 };
8703                 
8704             }
8705             
8706             combobox.cn.push({
8707                 tag :'span',
8708                 cls : 'input-group-addon btn dropdown-toggle',
8709                 cn : [
8710                     caret,
8711                     {
8712                         tag: 'span',
8713                         cls: 'combobox-clear',
8714                         cn  : [
8715                             {
8716                                 tag : 'i',
8717                                 cls: 'icon-remove'
8718                             }
8719                         ]
8720                     }
8721                 ]
8722
8723             })
8724         }
8725         
8726         if(this.multiple){
8727             combobox.cls += ' select2-container-multi';
8728         }
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732                 Roo.log("left and has label");
8733                 cfg.cn = [
8734                     
8735                     {
8736                         tag: 'label',
8737                         'for' :  id,
8738                         cls : 'control-label col-sm-' + this.labelWidth,
8739                         html : this.fieldLabel
8740                         
8741                     },
8742                     {
8743                         cls : "col-sm-" + (12 - this.labelWidth), 
8744                         cn: [
8745                             combobox
8746                         ]
8747                     }
8748                     
8749                 ];
8750         } else if ( this.fieldLabel.length) {
8751                 Roo.log(" label");
8752                  cfg.cn = [
8753                    
8754                     {
8755                         tag: 'label',
8756                         //cls : 'input-group-addon',
8757                         html : this.fieldLabel
8758                         
8759                     },
8760                     
8761                     combobox
8762                     
8763                 ];
8764
8765         } else {
8766             
8767                 Roo.log(" no label && no align");
8768                 cfg = combobox
8769                      
8770                 
8771         }
8772          
8773         var settings=this;
8774         ['xs','sm','md','lg'].map(function(size){
8775             if (settings[size]) {
8776                 cfg.cls += ' col-' + size + '-' + settings[size];
8777             }
8778         });
8779         
8780         return cfg;
8781         
8782     },
8783     
8784     
8785     
8786     // private
8787     onResize : function(w, h){
8788 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8789 //        if(typeof w == 'number'){
8790 //            var x = w - this.trigger.getWidth();
8791 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8792 //            this.trigger.setStyle('left', x+'px');
8793 //        }
8794     },
8795
8796     // private
8797     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8798
8799     // private
8800     getResizeEl : function(){
8801         return this.inputEl();
8802     },
8803
8804     // private
8805     getPositionEl : function(){
8806         return this.inputEl();
8807     },
8808
8809     // private
8810     alignErrorIcon : function(){
8811         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8812     },
8813
8814     // private
8815     initEvents : function(){
8816         
8817         this.createList();
8818         
8819         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8820         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8821         if(!this.multiple && this.showToggleBtn){
8822             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8823             if(this.hideTrigger){
8824                 this.trigger.setDisplayed(false);
8825             }
8826             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8827         }
8828         
8829         if(this.multiple){
8830             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8831         }
8832         
8833         if(this.removable && !this.editable && !this.tickable){
8834             var close = this.closeTriggerEl();
8835             
8836             if(close){
8837                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
8838                 close.on('click', this.removeBtnClick, this, close);
8839             }
8840         }
8841         
8842         //this.trigger.addClassOnOver('x-form-trigger-over');
8843         //this.trigger.addClassOnClick('x-form-trigger-click');
8844         
8845         //if(!this.width){
8846         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8847         //}
8848     },
8849     
8850     closeTriggerEl : function()
8851     {
8852         var close = this.el.select('.roo-combo-removable-btn', true).first();
8853         return close ? close : false;
8854     },
8855     
8856     removeBtnClick : function(e, h, el)
8857     {
8858         e.preventDefault();
8859         
8860         if(this.fireEvent("remove", this) !== false){
8861             this.reset();
8862         }
8863     },
8864     
8865     createList : function()
8866     {
8867         this.list = Roo.get(document.body).createChild({
8868             tag: 'ul',
8869             cls: 'typeahead typeahead-long dropdown-menu',
8870             style: 'display:none'
8871         });
8872         
8873         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8874         
8875     },
8876
8877     // private
8878     initTrigger : function(){
8879        
8880     },
8881
8882     // private
8883     onDestroy : function(){
8884         if(this.trigger){
8885             this.trigger.removeAllListeners();
8886           //  this.trigger.remove();
8887         }
8888         //if(this.wrap){
8889         //    this.wrap.remove();
8890         //}
8891         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8892     },
8893
8894     // private
8895     onFocus : function(){
8896         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8897         /*
8898         if(!this.mimicing){
8899             this.wrap.addClass('x-trigger-wrap-focus');
8900             this.mimicing = true;
8901             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8902             if(this.monitorTab){
8903                 this.el.on("keydown", this.checkTab, this);
8904             }
8905         }
8906         */
8907     },
8908
8909     // private
8910     checkTab : function(e){
8911         if(e.getKey() == e.TAB){
8912             this.triggerBlur();
8913         }
8914     },
8915
8916     // private
8917     onBlur : function(){
8918         // do nothing
8919     },
8920
8921     // private
8922     mimicBlur : function(e, t){
8923         /*
8924         if(!this.wrap.contains(t) && this.validateBlur()){
8925             this.triggerBlur();
8926         }
8927         */
8928     },
8929
8930     // private
8931     triggerBlur : function(){
8932         this.mimicing = false;
8933         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8934         if(this.monitorTab){
8935             this.el.un("keydown", this.checkTab, this);
8936         }
8937         //this.wrap.removeClass('x-trigger-wrap-focus');
8938         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8939     },
8940
8941     // private
8942     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8943     validateBlur : function(e, t){
8944         return true;
8945     },
8946
8947     // private
8948     onDisable : function(){
8949         this.inputEl().dom.disabled = true;
8950         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8951         //if(this.wrap){
8952         //    this.wrap.addClass('x-item-disabled');
8953         //}
8954     },
8955
8956     // private
8957     onEnable : function(){
8958         this.inputEl().dom.disabled = false;
8959         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8960         //if(this.wrap){
8961         //    this.el.removeClass('x-item-disabled');
8962         //}
8963     },
8964
8965     // private
8966     onShow : function(){
8967         var ae = this.getActionEl();
8968         
8969         if(ae){
8970             ae.dom.style.display = '';
8971             ae.dom.style.visibility = 'visible';
8972         }
8973     },
8974
8975     // private
8976     
8977     onHide : function(){
8978         var ae = this.getActionEl();
8979         ae.dom.style.display = 'none';
8980     },
8981
8982     /**
8983      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8984      * by an implementing function.
8985      * @method
8986      * @param {EventObject} e
8987      */
8988     onTriggerClick : Roo.emptyFn
8989 });
8990  /*
8991  * Based on:
8992  * Ext JS Library 1.1.1
8993  * Copyright(c) 2006-2007, Ext JS, LLC.
8994  *
8995  * Originally Released Under LGPL - original licence link has changed is not relivant.
8996  *
8997  * Fork - LGPL
8998  * <script type="text/javascript">
8999  */
9000
9001
9002 /**
9003  * @class Roo.data.SortTypes
9004  * @singleton
9005  * Defines the default sorting (casting?) comparison functions used when sorting data.
9006  */
9007 Roo.data.SortTypes = {
9008     /**
9009      * Default sort that does nothing
9010      * @param {Mixed} s The value being converted
9011      * @return {Mixed} The comparison value
9012      */
9013     none : function(s){
9014         return s;
9015     },
9016     
9017     /**
9018      * The regular expression used to strip tags
9019      * @type {RegExp}
9020      * @property
9021      */
9022     stripTagsRE : /<\/?[^>]+>/gi,
9023     
9024     /**
9025      * Strips all HTML tags to sort on text only
9026      * @param {Mixed} s The value being converted
9027      * @return {String} The comparison value
9028      */
9029     asText : function(s){
9030         return String(s).replace(this.stripTagsRE, "");
9031     },
9032     
9033     /**
9034      * Strips all HTML tags to sort on text only - Case insensitive
9035      * @param {Mixed} s The value being converted
9036      * @return {String} The comparison value
9037      */
9038     asUCText : function(s){
9039         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9040     },
9041     
9042     /**
9043      * Case insensitive string
9044      * @param {Mixed} s The value being converted
9045      * @return {String} The comparison value
9046      */
9047     asUCString : function(s) {
9048         return String(s).toUpperCase();
9049     },
9050     
9051     /**
9052      * Date sorting
9053      * @param {Mixed} s The value being converted
9054      * @return {Number} The comparison value
9055      */
9056     asDate : function(s) {
9057         if(!s){
9058             return 0;
9059         }
9060         if(s instanceof Date){
9061             return s.getTime();
9062         }
9063         return Date.parse(String(s));
9064     },
9065     
9066     /**
9067      * Float sorting
9068      * @param {Mixed} s The value being converted
9069      * @return {Float} The comparison value
9070      */
9071     asFloat : function(s) {
9072         var val = parseFloat(String(s).replace(/,/g, ""));
9073         if(isNaN(val)) val = 0;
9074         return val;
9075     },
9076     
9077     /**
9078      * Integer sorting
9079      * @param {Mixed} s The value being converted
9080      * @return {Number} The comparison value
9081      */
9082     asInt : function(s) {
9083         var val = parseInt(String(s).replace(/,/g, ""));
9084         if(isNaN(val)) val = 0;
9085         return val;
9086     }
9087 };/*
9088  * Based on:
9089  * Ext JS Library 1.1.1
9090  * Copyright(c) 2006-2007, Ext JS, LLC.
9091  *
9092  * Originally Released Under LGPL - original licence link has changed is not relivant.
9093  *
9094  * Fork - LGPL
9095  * <script type="text/javascript">
9096  */
9097
9098 /**
9099 * @class Roo.data.Record
9100  * Instances of this class encapsulate both record <em>definition</em> information, and record
9101  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9102  * to access Records cached in an {@link Roo.data.Store} object.<br>
9103  * <p>
9104  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9105  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9106  * objects.<br>
9107  * <p>
9108  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9109  * @constructor
9110  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9111  * {@link #create}. The parameters are the same.
9112  * @param {Array} data An associative Array of data values keyed by the field name.
9113  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9114  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9115  * not specified an integer id is generated.
9116  */
9117 Roo.data.Record = function(data, id){
9118     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9119     this.data = data;
9120 };
9121
9122 /**
9123  * Generate a constructor for a specific record layout.
9124  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9125  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9126  * Each field definition object may contain the following properties: <ul>
9127  * <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,
9128  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9129  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9130  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9131  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9132  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9133  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9134  * this may be omitted.</p></li>
9135  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9136  * <ul><li>auto (Default, implies no conversion)</li>
9137  * <li>string</li>
9138  * <li>int</li>
9139  * <li>float</li>
9140  * <li>boolean</li>
9141  * <li>date</li></ul></p></li>
9142  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9143  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9144  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9145  * by the Reader into an object that will be stored in the Record. It is passed the
9146  * following parameters:<ul>
9147  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9148  * </ul></p></li>
9149  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9150  * </ul>
9151  * <br>usage:<br><pre><code>
9152 var TopicRecord = Roo.data.Record.create(
9153     {name: 'title', mapping: 'topic_title'},
9154     {name: 'author', mapping: 'username'},
9155     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9156     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9157     {name: 'lastPoster', mapping: 'user2'},
9158     {name: 'excerpt', mapping: 'post_text'}
9159 );
9160
9161 var myNewRecord = new TopicRecord({
9162     title: 'Do my job please',
9163     author: 'noobie',
9164     totalPosts: 1,
9165     lastPost: new Date(),
9166     lastPoster: 'Animal',
9167     excerpt: 'No way dude!'
9168 });
9169 myStore.add(myNewRecord);
9170 </code></pre>
9171  * @method create
9172  * @static
9173  */
9174 Roo.data.Record.create = function(o){
9175     var f = function(){
9176         f.superclass.constructor.apply(this, arguments);
9177     };
9178     Roo.extend(f, Roo.data.Record);
9179     var p = f.prototype;
9180     p.fields = new Roo.util.MixedCollection(false, function(field){
9181         return field.name;
9182     });
9183     for(var i = 0, len = o.length; i < len; i++){
9184         p.fields.add(new Roo.data.Field(o[i]));
9185     }
9186     f.getField = function(name){
9187         return p.fields.get(name);  
9188     };
9189     return f;
9190 };
9191
9192 Roo.data.Record.AUTO_ID = 1000;
9193 Roo.data.Record.EDIT = 'edit';
9194 Roo.data.Record.REJECT = 'reject';
9195 Roo.data.Record.COMMIT = 'commit';
9196
9197 Roo.data.Record.prototype = {
9198     /**
9199      * Readonly flag - true if this record has been modified.
9200      * @type Boolean
9201      */
9202     dirty : false,
9203     editing : false,
9204     error: null,
9205     modified: null,
9206
9207     // private
9208     join : function(store){
9209         this.store = store;
9210     },
9211
9212     /**
9213      * Set the named field to the specified value.
9214      * @param {String} name The name of the field to set.
9215      * @param {Object} value The value to set the field to.
9216      */
9217     set : function(name, value){
9218         if(this.data[name] == value){
9219             return;
9220         }
9221         this.dirty = true;
9222         if(!this.modified){
9223             this.modified = {};
9224         }
9225         if(typeof this.modified[name] == 'undefined'){
9226             this.modified[name] = this.data[name];
9227         }
9228         this.data[name] = value;
9229         if(!this.editing && this.store){
9230             this.store.afterEdit(this);
9231         }       
9232     },
9233
9234     /**
9235      * Get the value of the named field.
9236      * @param {String} name The name of the field to get the value of.
9237      * @return {Object} The value of the field.
9238      */
9239     get : function(name){
9240         return this.data[name]; 
9241     },
9242
9243     // private
9244     beginEdit : function(){
9245         this.editing = true;
9246         this.modified = {}; 
9247     },
9248
9249     // private
9250     cancelEdit : function(){
9251         this.editing = false;
9252         delete this.modified;
9253     },
9254
9255     // private
9256     endEdit : function(){
9257         this.editing = false;
9258         if(this.dirty && this.store){
9259             this.store.afterEdit(this);
9260         }
9261     },
9262
9263     /**
9264      * Usually called by the {@link Roo.data.Store} which owns the Record.
9265      * Rejects all changes made to the Record since either creation, or the last commit operation.
9266      * Modified fields are reverted to their original values.
9267      * <p>
9268      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9269      * of reject operations.
9270      */
9271     reject : function(){
9272         var m = this.modified;
9273         for(var n in m){
9274             if(typeof m[n] != "function"){
9275                 this.data[n] = m[n];
9276             }
9277         }
9278         this.dirty = false;
9279         delete this.modified;
9280         this.editing = false;
9281         if(this.store){
9282             this.store.afterReject(this);
9283         }
9284     },
9285
9286     /**
9287      * Usually called by the {@link Roo.data.Store} which owns the Record.
9288      * Commits all changes made to the Record since either creation, or the last commit operation.
9289      * <p>
9290      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9291      * of commit operations.
9292      */
9293     commit : function(){
9294         this.dirty = false;
9295         delete this.modified;
9296         this.editing = false;
9297         if(this.store){
9298             this.store.afterCommit(this);
9299         }
9300     },
9301
9302     // private
9303     hasError : function(){
9304         return this.error != null;
9305     },
9306
9307     // private
9308     clearError : function(){
9309         this.error = null;
9310     },
9311
9312     /**
9313      * Creates a copy of this record.
9314      * @param {String} id (optional) A new record id if you don't want to use this record's id
9315      * @return {Record}
9316      */
9317     copy : function(newId) {
9318         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9319     }
9320 };/*
9321  * Based on:
9322  * Ext JS Library 1.1.1
9323  * Copyright(c) 2006-2007, Ext JS, LLC.
9324  *
9325  * Originally Released Under LGPL - original licence link has changed is not relivant.
9326  *
9327  * Fork - LGPL
9328  * <script type="text/javascript">
9329  */
9330
9331
9332
9333 /**
9334  * @class Roo.data.Store
9335  * @extends Roo.util.Observable
9336  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9337  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9338  * <p>
9339  * 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
9340  * has no knowledge of the format of the data returned by the Proxy.<br>
9341  * <p>
9342  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9343  * instances from the data object. These records are cached and made available through accessor functions.
9344  * @constructor
9345  * Creates a new Store.
9346  * @param {Object} config A config object containing the objects needed for the Store to access data,
9347  * and read the data into Records.
9348  */
9349 Roo.data.Store = function(config){
9350     this.data = new Roo.util.MixedCollection(false);
9351     this.data.getKey = function(o){
9352         return o.id;
9353     };
9354     this.baseParams = {};
9355     // private
9356     this.paramNames = {
9357         "start" : "start",
9358         "limit" : "limit",
9359         "sort" : "sort",
9360         "dir" : "dir",
9361         "multisort" : "_multisort"
9362     };
9363
9364     if(config && config.data){
9365         this.inlineData = config.data;
9366         delete config.data;
9367     }
9368
9369     Roo.apply(this, config);
9370     
9371     if(this.reader){ // reader passed
9372         this.reader = Roo.factory(this.reader, Roo.data);
9373         this.reader.xmodule = this.xmodule || false;
9374         if(!this.recordType){
9375             this.recordType = this.reader.recordType;
9376         }
9377         if(this.reader.onMetaChange){
9378             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9379         }
9380     }
9381
9382     if(this.recordType){
9383         this.fields = this.recordType.prototype.fields;
9384     }
9385     this.modified = [];
9386
9387     this.addEvents({
9388         /**
9389          * @event datachanged
9390          * Fires when the data cache has changed, and a widget which is using this Store
9391          * as a Record cache should refresh its view.
9392          * @param {Store} this
9393          */
9394         datachanged : true,
9395         /**
9396          * @event metachange
9397          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9398          * @param {Store} this
9399          * @param {Object} meta The JSON metadata
9400          */
9401         metachange : true,
9402         /**
9403          * @event add
9404          * Fires when Records have been added to the Store
9405          * @param {Store} this
9406          * @param {Roo.data.Record[]} records The array of Records added
9407          * @param {Number} index The index at which the record(s) were added
9408          */
9409         add : true,
9410         /**
9411          * @event remove
9412          * Fires when a Record has been removed from the Store
9413          * @param {Store} this
9414          * @param {Roo.data.Record} record The Record that was removed
9415          * @param {Number} index The index at which the record was removed
9416          */
9417         remove : true,
9418         /**
9419          * @event update
9420          * Fires when a Record has been updated
9421          * @param {Store} this
9422          * @param {Roo.data.Record} record The Record that was updated
9423          * @param {String} operation The update operation being performed.  Value may be one of:
9424          * <pre><code>
9425  Roo.data.Record.EDIT
9426  Roo.data.Record.REJECT
9427  Roo.data.Record.COMMIT
9428          * </code></pre>
9429          */
9430         update : true,
9431         /**
9432          * @event clear
9433          * Fires when the data cache has been cleared.
9434          * @param {Store} this
9435          */
9436         clear : true,
9437         /**
9438          * @event beforeload
9439          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9440          * the load action will be canceled.
9441          * @param {Store} this
9442          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9443          */
9444         beforeload : true,
9445         /**
9446          * @event beforeloadadd
9447          * Fires after a new set of Records has been loaded.
9448          * @param {Store} this
9449          * @param {Roo.data.Record[]} records The Records that were loaded
9450          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9451          */
9452         beforeloadadd : true,
9453         /**
9454          * @event load
9455          * Fires after a new set of Records has been loaded, before they are added to the store.
9456          * @param {Store} this
9457          * @param {Roo.data.Record[]} records The Records that were loaded
9458          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9459          * @params {Object} return from reader
9460          */
9461         load : true,
9462         /**
9463          * @event loadexception
9464          * Fires if an exception occurs in the Proxy during loading.
9465          * Called with the signature of the Proxy's "loadexception" event.
9466          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9467          * 
9468          * @param {Proxy} 
9469          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9470          * @param {Object} load options 
9471          * @param {Object} jsonData from your request (normally this contains the Exception)
9472          */
9473         loadexception : true
9474     });
9475     
9476     if(this.proxy){
9477         this.proxy = Roo.factory(this.proxy, Roo.data);
9478         this.proxy.xmodule = this.xmodule || false;
9479         this.relayEvents(this.proxy,  ["loadexception"]);
9480     }
9481     this.sortToggle = {};
9482     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9483
9484     Roo.data.Store.superclass.constructor.call(this);
9485
9486     if(this.inlineData){
9487         this.loadData(this.inlineData);
9488         delete this.inlineData;
9489     }
9490 };
9491
9492 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9493      /**
9494     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9495     * without a remote query - used by combo/forms at present.
9496     */
9497     
9498     /**
9499     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9500     */
9501     /**
9502     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9503     */
9504     /**
9505     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9506     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9507     */
9508     /**
9509     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9510     * on any HTTP request
9511     */
9512     /**
9513     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9514     */
9515     /**
9516     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9517     */
9518     multiSort: false,
9519     /**
9520     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9521     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9522     */
9523     remoteSort : false,
9524
9525     /**
9526     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9527      * loaded or when a record is removed. (defaults to false).
9528     */
9529     pruneModifiedRecords : false,
9530
9531     // private
9532     lastOptions : null,
9533
9534     /**
9535      * Add Records to the Store and fires the add event.
9536      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9537      */
9538     add : function(records){
9539         records = [].concat(records);
9540         for(var i = 0, len = records.length; i < len; i++){
9541             records[i].join(this);
9542         }
9543         var index = this.data.length;
9544         this.data.addAll(records);
9545         this.fireEvent("add", this, records, index);
9546     },
9547
9548     /**
9549      * Remove a Record from the Store and fires the remove event.
9550      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9551      */
9552     remove : function(record){
9553         var index = this.data.indexOf(record);
9554         this.data.removeAt(index);
9555         if(this.pruneModifiedRecords){
9556             this.modified.remove(record);
9557         }
9558         this.fireEvent("remove", this, record, index);
9559     },
9560
9561     /**
9562      * Remove all Records from the Store and fires the clear event.
9563      */
9564     removeAll : function(){
9565         this.data.clear();
9566         if(this.pruneModifiedRecords){
9567             this.modified = [];
9568         }
9569         this.fireEvent("clear", this);
9570     },
9571
9572     /**
9573      * Inserts Records to the Store at the given index and fires the add event.
9574      * @param {Number} index The start index at which to insert the passed Records.
9575      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9576      */
9577     insert : function(index, records){
9578         records = [].concat(records);
9579         for(var i = 0, len = records.length; i < len; i++){
9580             this.data.insert(index, records[i]);
9581             records[i].join(this);
9582         }
9583         this.fireEvent("add", this, records, index);
9584     },
9585
9586     /**
9587      * Get the index within the cache of the passed Record.
9588      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9589      * @return {Number} The index of the passed Record. Returns -1 if not found.
9590      */
9591     indexOf : function(record){
9592         return this.data.indexOf(record);
9593     },
9594
9595     /**
9596      * Get the index within the cache of the Record with the passed id.
9597      * @param {String} id The id of the Record to find.
9598      * @return {Number} The index of the Record. Returns -1 if not found.
9599      */
9600     indexOfId : function(id){
9601         return this.data.indexOfKey(id);
9602     },
9603
9604     /**
9605      * Get the Record with the specified id.
9606      * @param {String} id The id of the Record to find.
9607      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9608      */
9609     getById : function(id){
9610         return this.data.key(id);
9611     },
9612
9613     /**
9614      * Get the Record at the specified index.
9615      * @param {Number} index The index of the Record to find.
9616      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9617      */
9618     getAt : function(index){
9619         return this.data.itemAt(index);
9620     },
9621
9622     /**
9623      * Returns a range of Records between specified indices.
9624      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9625      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9626      * @return {Roo.data.Record[]} An array of Records
9627      */
9628     getRange : function(start, end){
9629         return this.data.getRange(start, end);
9630     },
9631
9632     // private
9633     storeOptions : function(o){
9634         o = Roo.apply({}, o);
9635         delete o.callback;
9636         delete o.scope;
9637         this.lastOptions = o;
9638     },
9639
9640     /**
9641      * Loads the Record cache from the configured Proxy using the configured Reader.
9642      * <p>
9643      * If using remote paging, then the first load call must specify the <em>start</em>
9644      * and <em>limit</em> properties in the options.params property to establish the initial
9645      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9646      * <p>
9647      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9648      * and this call will return before the new data has been loaded. Perform any post-processing
9649      * in a callback function, or in a "load" event handler.</strong>
9650      * <p>
9651      * @param {Object} options An object containing properties which control loading options:<ul>
9652      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9653      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9654      * passed the following arguments:<ul>
9655      * <li>r : Roo.data.Record[]</li>
9656      * <li>options: Options object from the load call</li>
9657      * <li>success: Boolean success indicator</li></ul></li>
9658      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9659      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9660      * </ul>
9661      */
9662     load : function(options){
9663         options = options || {};
9664         if(this.fireEvent("beforeload", this, options) !== false){
9665             this.storeOptions(options);
9666             var p = Roo.apply(options.params || {}, this.baseParams);
9667             // if meta was not loaded from remote source.. try requesting it.
9668             if (!this.reader.metaFromRemote) {
9669                 p._requestMeta = 1;
9670             }
9671             if(this.sortInfo && this.remoteSort){
9672                 var pn = this.paramNames;
9673                 p[pn["sort"]] = this.sortInfo.field;
9674                 p[pn["dir"]] = this.sortInfo.direction;
9675             }
9676             if (this.multiSort) {
9677                 var pn = this.paramNames;
9678                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9679             }
9680             
9681             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9682         }
9683     },
9684
9685     /**
9686      * Reloads the Record cache from the configured Proxy using the configured Reader and
9687      * the options from the last load operation performed.
9688      * @param {Object} options (optional) An object containing properties which may override the options
9689      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9690      * the most recently used options are reused).
9691      */
9692     reload : function(options){
9693         this.load(Roo.applyIf(options||{}, this.lastOptions));
9694     },
9695
9696     // private
9697     // Called as a callback by the Reader during a load operation.
9698     loadRecords : function(o, options, success){
9699         if(!o || success === false){
9700             if(success !== false){
9701                 this.fireEvent("load", this, [], options, o);
9702             }
9703             if(options.callback){
9704                 options.callback.call(options.scope || this, [], options, false);
9705             }
9706             return;
9707         }
9708         // if data returned failure - throw an exception.
9709         if (o.success === false) {
9710             // show a message if no listener is registered.
9711             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9712                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9713             }
9714             // loadmask wil be hooked into this..
9715             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9716             return;
9717         }
9718         var r = o.records, t = o.totalRecords || r.length;
9719         
9720         this.fireEvent("beforeloadadd", this, r, options, o);
9721         
9722         if(!options || options.add !== true){
9723             if(this.pruneModifiedRecords){
9724                 this.modified = [];
9725             }
9726             for(var i = 0, len = r.length; i < len; i++){
9727                 r[i].join(this);
9728             }
9729             if(this.snapshot){
9730                 this.data = this.snapshot;
9731                 delete this.snapshot;
9732             }
9733             this.data.clear();
9734             this.data.addAll(r);
9735             this.totalLength = t;
9736             this.applySort();
9737             this.fireEvent("datachanged", this);
9738         }else{
9739             this.totalLength = Math.max(t, this.data.length+r.length);
9740             this.add(r);
9741         }
9742         this.fireEvent("load", this, r, options, o);
9743         if(options.callback){
9744             options.callback.call(options.scope || this, r, options, true);
9745         }
9746     },
9747
9748
9749     /**
9750      * Loads data from a passed data block. A Reader which understands the format of the data
9751      * must have been configured in the constructor.
9752      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9753      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9754      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9755      */
9756     loadData : function(o, append){
9757         var r = this.reader.readRecords(o);
9758         this.loadRecords(r, {add: append}, true);
9759     },
9760
9761     /**
9762      * Gets the number of cached records.
9763      * <p>
9764      * <em>If using paging, this may not be the total size of the dataset. If the data object
9765      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9766      * the data set size</em>
9767      */
9768     getCount : function(){
9769         return this.data.length || 0;
9770     },
9771
9772     /**
9773      * Gets the total number of records in the dataset as returned by the server.
9774      * <p>
9775      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9776      * the dataset size</em>
9777      */
9778     getTotalCount : function(){
9779         return this.totalLength || 0;
9780     },
9781
9782     /**
9783      * Returns the sort state of the Store as an object with two properties:
9784      * <pre><code>
9785  field {String} The name of the field by which the Records are sorted
9786  direction {String} The sort order, "ASC" or "DESC"
9787      * </code></pre>
9788      */
9789     getSortState : function(){
9790         return this.sortInfo;
9791     },
9792
9793     // private
9794     applySort : function(){
9795         if(this.sortInfo && !this.remoteSort){
9796             var s = this.sortInfo, f = s.field;
9797             var st = this.fields.get(f).sortType;
9798             var fn = function(r1, r2){
9799                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9800                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9801             };
9802             this.data.sort(s.direction, fn);
9803             if(this.snapshot && this.snapshot != this.data){
9804                 this.snapshot.sort(s.direction, fn);
9805             }
9806         }
9807     },
9808
9809     /**
9810      * Sets the default sort column and order to be used by the next load operation.
9811      * @param {String} fieldName The name of the field to sort by.
9812      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9813      */
9814     setDefaultSort : function(field, dir){
9815         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9816     },
9817
9818     /**
9819      * Sort the Records.
9820      * If remote sorting is used, the sort is performed on the server, and the cache is
9821      * reloaded. If local sorting is used, the cache is sorted internally.
9822      * @param {String} fieldName The name of the field to sort by.
9823      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9824      */
9825     sort : function(fieldName, dir){
9826         var f = this.fields.get(fieldName);
9827         if(!dir){
9828             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9829             
9830             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9831                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9832             }else{
9833                 dir = f.sortDir;
9834             }
9835         }
9836         this.sortToggle[f.name] = dir;
9837         this.sortInfo = {field: f.name, direction: dir};
9838         if(!this.remoteSort){
9839             this.applySort();
9840             this.fireEvent("datachanged", this);
9841         }else{
9842             this.load(this.lastOptions);
9843         }
9844     },
9845
9846     /**
9847      * Calls the specified function for each of the Records in the cache.
9848      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9849      * Returning <em>false</em> aborts and exits the iteration.
9850      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9851      */
9852     each : function(fn, scope){
9853         this.data.each(fn, scope);
9854     },
9855
9856     /**
9857      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9858      * (e.g., during paging).
9859      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9860      */
9861     getModifiedRecords : function(){
9862         return this.modified;
9863     },
9864
9865     // private
9866     createFilterFn : function(property, value, anyMatch){
9867         if(!value.exec){ // not a regex
9868             value = String(value);
9869             if(value.length == 0){
9870                 return false;
9871             }
9872             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9873         }
9874         return function(r){
9875             return value.test(r.data[property]);
9876         };
9877     },
9878
9879     /**
9880      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9881      * @param {String} property A field on your records
9882      * @param {Number} start The record index to start at (defaults to 0)
9883      * @param {Number} end The last record index to include (defaults to length - 1)
9884      * @return {Number} The sum
9885      */
9886     sum : function(property, start, end){
9887         var rs = this.data.items, v = 0;
9888         start = start || 0;
9889         end = (end || end === 0) ? end : rs.length-1;
9890
9891         for(var i = start; i <= end; i++){
9892             v += (rs[i].data[property] || 0);
9893         }
9894         return v;
9895     },
9896
9897     /**
9898      * Filter the records by a specified property.
9899      * @param {String} field A field on your records
9900      * @param {String/RegExp} value Either a string that the field
9901      * should start with or a RegExp to test against the field
9902      * @param {Boolean} anyMatch True to match any part not just the beginning
9903      */
9904     filter : function(property, value, anyMatch){
9905         var fn = this.createFilterFn(property, value, anyMatch);
9906         return fn ? this.filterBy(fn) : this.clearFilter();
9907     },
9908
9909     /**
9910      * Filter by a function. The specified function will be called with each
9911      * record in this data source. If the function returns true the record is included,
9912      * otherwise it is filtered.
9913      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9914      * @param {Object} scope (optional) The scope of the function (defaults to this)
9915      */
9916     filterBy : function(fn, scope){
9917         this.snapshot = this.snapshot || this.data;
9918         this.data = this.queryBy(fn, scope||this);
9919         this.fireEvent("datachanged", this);
9920     },
9921
9922     /**
9923      * Query the records by a specified property.
9924      * @param {String} field A field on your records
9925      * @param {String/RegExp} value Either a string that the field
9926      * should start with or a RegExp to test against the field
9927      * @param {Boolean} anyMatch True to match any part not just the beginning
9928      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9929      */
9930     query : function(property, value, anyMatch){
9931         var fn = this.createFilterFn(property, value, anyMatch);
9932         return fn ? this.queryBy(fn) : this.data.clone();
9933     },
9934
9935     /**
9936      * Query by a function. The specified function will be called with each
9937      * record in this data source. If the function returns true the record is included
9938      * in the results.
9939      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9940      * @param {Object} scope (optional) The scope of the function (defaults to this)
9941       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9942      **/
9943     queryBy : function(fn, scope){
9944         var data = this.snapshot || this.data;
9945         return data.filterBy(fn, scope||this);
9946     },
9947
9948     /**
9949      * Collects unique values for a particular dataIndex from this store.
9950      * @param {String} dataIndex The property to collect
9951      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9952      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9953      * @return {Array} An array of the unique values
9954      **/
9955     collect : function(dataIndex, allowNull, bypassFilter){
9956         var d = (bypassFilter === true && this.snapshot) ?
9957                 this.snapshot.items : this.data.items;
9958         var v, sv, r = [], l = {};
9959         for(var i = 0, len = d.length; i < len; i++){
9960             v = d[i].data[dataIndex];
9961             sv = String(v);
9962             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9963                 l[sv] = true;
9964                 r[r.length] = v;
9965             }
9966         }
9967         return r;
9968     },
9969
9970     /**
9971      * Revert to a view of the Record cache with no filtering applied.
9972      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9973      */
9974     clearFilter : function(suppressEvent){
9975         if(this.snapshot && this.snapshot != this.data){
9976             this.data = this.snapshot;
9977             delete this.snapshot;
9978             if(suppressEvent !== true){
9979                 this.fireEvent("datachanged", this);
9980             }
9981         }
9982     },
9983
9984     // private
9985     afterEdit : function(record){
9986         if(this.modified.indexOf(record) == -1){
9987             this.modified.push(record);
9988         }
9989         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9990     },
9991     
9992     // private
9993     afterReject : function(record){
9994         this.modified.remove(record);
9995         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9996     },
9997
9998     // private
9999     afterCommit : function(record){
10000         this.modified.remove(record);
10001         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10002     },
10003
10004     /**
10005      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10006      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10007      */
10008     commitChanges : function(){
10009         var m = this.modified.slice(0);
10010         this.modified = [];
10011         for(var i = 0, len = m.length; i < len; i++){
10012             m[i].commit();
10013         }
10014     },
10015
10016     /**
10017      * Cancel outstanding changes on all changed records.
10018      */
10019     rejectChanges : function(){
10020         var m = this.modified.slice(0);
10021         this.modified = [];
10022         for(var i = 0, len = m.length; i < len; i++){
10023             m[i].reject();
10024         }
10025     },
10026
10027     onMetaChange : function(meta, rtype, o){
10028         this.recordType = rtype;
10029         this.fields = rtype.prototype.fields;
10030         delete this.snapshot;
10031         this.sortInfo = meta.sortInfo || this.sortInfo;
10032         this.modified = [];
10033         this.fireEvent('metachange', this, this.reader.meta);
10034     },
10035     
10036     moveIndex : function(data, type)
10037     {
10038         var index = this.indexOf(data);
10039         
10040         var newIndex = index + type;
10041         
10042         this.remove(data);
10043         
10044         this.insert(newIndex, data);
10045         
10046     }
10047 });/*
10048  * Based on:
10049  * Ext JS Library 1.1.1
10050  * Copyright(c) 2006-2007, Ext JS, LLC.
10051  *
10052  * Originally Released Under LGPL - original licence link has changed is not relivant.
10053  *
10054  * Fork - LGPL
10055  * <script type="text/javascript">
10056  */
10057
10058 /**
10059  * @class Roo.data.SimpleStore
10060  * @extends Roo.data.Store
10061  * Small helper class to make creating Stores from Array data easier.
10062  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10063  * @cfg {Array} fields An array of field definition objects, or field name strings.
10064  * @cfg {Array} data The multi-dimensional array of data
10065  * @constructor
10066  * @param {Object} config
10067  */
10068 Roo.data.SimpleStore = function(config){
10069     Roo.data.SimpleStore.superclass.constructor.call(this, {
10070         isLocal : true,
10071         reader: new Roo.data.ArrayReader({
10072                 id: config.id
10073             },
10074             Roo.data.Record.create(config.fields)
10075         ),
10076         proxy : new Roo.data.MemoryProxy(config.data)
10077     });
10078     this.load();
10079 };
10080 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10081  * Based on:
10082  * Ext JS Library 1.1.1
10083  * Copyright(c) 2006-2007, Ext JS, LLC.
10084  *
10085  * Originally Released Under LGPL - original licence link has changed is not relivant.
10086  *
10087  * Fork - LGPL
10088  * <script type="text/javascript">
10089  */
10090
10091 /**
10092 /**
10093  * @extends Roo.data.Store
10094  * @class Roo.data.JsonStore
10095  * Small helper class to make creating Stores for JSON data easier. <br/>
10096 <pre><code>
10097 var store = new Roo.data.JsonStore({
10098     url: 'get-images.php',
10099     root: 'images',
10100     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10101 });
10102 </code></pre>
10103  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10104  * JsonReader and HttpProxy (unless inline data is provided).</b>
10105  * @cfg {Array} fields An array of field definition objects, or field name strings.
10106  * @constructor
10107  * @param {Object} config
10108  */
10109 Roo.data.JsonStore = function(c){
10110     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10111         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10112         reader: new Roo.data.JsonReader(c, c.fields)
10113     }));
10114 };
10115 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10116  * Based on:
10117  * Ext JS Library 1.1.1
10118  * Copyright(c) 2006-2007, Ext JS, LLC.
10119  *
10120  * Originally Released Under LGPL - original licence link has changed is not relivant.
10121  *
10122  * Fork - LGPL
10123  * <script type="text/javascript">
10124  */
10125
10126  
10127 Roo.data.Field = function(config){
10128     if(typeof config == "string"){
10129         config = {name: config};
10130     }
10131     Roo.apply(this, config);
10132     
10133     if(!this.type){
10134         this.type = "auto";
10135     }
10136     
10137     var st = Roo.data.SortTypes;
10138     // named sortTypes are supported, here we look them up
10139     if(typeof this.sortType == "string"){
10140         this.sortType = st[this.sortType];
10141     }
10142     
10143     // set default sortType for strings and dates
10144     if(!this.sortType){
10145         switch(this.type){
10146             case "string":
10147                 this.sortType = st.asUCString;
10148                 break;
10149             case "date":
10150                 this.sortType = st.asDate;
10151                 break;
10152             default:
10153                 this.sortType = st.none;
10154         }
10155     }
10156
10157     // define once
10158     var stripRe = /[\$,%]/g;
10159
10160     // prebuilt conversion function for this field, instead of
10161     // switching every time we're reading a value
10162     if(!this.convert){
10163         var cv, dateFormat = this.dateFormat;
10164         switch(this.type){
10165             case "":
10166             case "auto":
10167             case undefined:
10168                 cv = function(v){ return v; };
10169                 break;
10170             case "string":
10171                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10172                 break;
10173             case "int":
10174                 cv = function(v){
10175                     return v !== undefined && v !== null && v !== '' ?
10176                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10177                     };
10178                 break;
10179             case "float":
10180                 cv = function(v){
10181                     return v !== undefined && v !== null && v !== '' ?
10182                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10183                     };
10184                 break;
10185             case "bool":
10186             case "boolean":
10187                 cv = function(v){ return v === true || v === "true" || v == 1; };
10188                 break;
10189             case "date":
10190                 cv = function(v){
10191                     if(!v){
10192                         return '';
10193                     }
10194                     if(v instanceof Date){
10195                         return v;
10196                     }
10197                     if(dateFormat){
10198                         if(dateFormat == "timestamp"){
10199                             return new Date(v*1000);
10200                         }
10201                         return Date.parseDate(v, dateFormat);
10202                     }
10203                     var parsed = Date.parse(v);
10204                     return parsed ? new Date(parsed) : null;
10205                 };
10206              break;
10207             
10208         }
10209         this.convert = cv;
10210     }
10211 };
10212
10213 Roo.data.Field.prototype = {
10214     dateFormat: null,
10215     defaultValue: "",
10216     mapping: null,
10217     sortType : null,
10218     sortDir : "ASC"
10219 };/*
10220  * Based on:
10221  * Ext JS Library 1.1.1
10222  * Copyright(c) 2006-2007, Ext JS, LLC.
10223  *
10224  * Originally Released Under LGPL - original licence link has changed is not relivant.
10225  *
10226  * Fork - LGPL
10227  * <script type="text/javascript">
10228  */
10229  
10230 // Base class for reading structured data from a data source.  This class is intended to be
10231 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10232
10233 /**
10234  * @class Roo.data.DataReader
10235  * Base class for reading structured data from a data source.  This class is intended to be
10236  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10237  */
10238
10239 Roo.data.DataReader = function(meta, recordType){
10240     
10241     this.meta = meta;
10242     
10243     this.recordType = recordType instanceof Array ? 
10244         Roo.data.Record.create(recordType) : recordType;
10245 };
10246
10247 Roo.data.DataReader.prototype = {
10248      /**
10249      * Create an empty record
10250      * @param {Object} data (optional) - overlay some values
10251      * @return {Roo.data.Record} record created.
10252      */
10253     newRow :  function(d) {
10254         var da =  {};
10255         this.recordType.prototype.fields.each(function(c) {
10256             switch( c.type) {
10257                 case 'int' : da[c.name] = 0; break;
10258                 case 'date' : da[c.name] = new Date(); break;
10259                 case 'float' : da[c.name] = 0.0; break;
10260                 case 'boolean' : da[c.name] = false; break;
10261                 default : da[c.name] = ""; break;
10262             }
10263             
10264         });
10265         return new this.recordType(Roo.apply(da, d));
10266     }
10267     
10268 };/*
10269  * Based on:
10270  * Ext JS Library 1.1.1
10271  * Copyright(c) 2006-2007, Ext JS, LLC.
10272  *
10273  * Originally Released Under LGPL - original licence link has changed is not relivant.
10274  *
10275  * Fork - LGPL
10276  * <script type="text/javascript">
10277  */
10278
10279 /**
10280  * @class Roo.data.DataProxy
10281  * @extends Roo.data.Observable
10282  * This class is an abstract base class for implementations which provide retrieval of
10283  * unformatted data objects.<br>
10284  * <p>
10285  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10286  * (of the appropriate type which knows how to parse the data object) to provide a block of
10287  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10288  * <p>
10289  * Custom implementations must implement the load method as described in
10290  * {@link Roo.data.HttpProxy#load}.
10291  */
10292 Roo.data.DataProxy = function(){
10293     this.addEvents({
10294         /**
10295          * @event beforeload
10296          * Fires before a network request is made to retrieve a data object.
10297          * @param {Object} This DataProxy object.
10298          * @param {Object} params The params parameter to the load function.
10299          */
10300         beforeload : true,
10301         /**
10302          * @event load
10303          * Fires before the load method's callback is called.
10304          * @param {Object} This DataProxy object.
10305          * @param {Object} o The data object.
10306          * @param {Object} arg The callback argument object passed to the load function.
10307          */
10308         load : true,
10309         /**
10310          * @event loadexception
10311          * Fires if an Exception occurs during data retrieval.
10312          * @param {Object} This DataProxy object.
10313          * @param {Object} o The data object.
10314          * @param {Object} arg The callback argument object passed to the load function.
10315          * @param {Object} e The Exception.
10316          */
10317         loadexception : true
10318     });
10319     Roo.data.DataProxy.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10323
10324     /**
10325      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10326      */
10327 /*
10328  * Based on:
10329  * Ext JS Library 1.1.1
10330  * Copyright(c) 2006-2007, Ext JS, LLC.
10331  *
10332  * Originally Released Under LGPL - original licence link has changed is not relivant.
10333  *
10334  * Fork - LGPL
10335  * <script type="text/javascript">
10336  */
10337 /**
10338  * @class Roo.data.MemoryProxy
10339  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10340  * to the Reader when its load method is called.
10341  * @constructor
10342  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10343  */
10344 Roo.data.MemoryProxy = function(data){
10345     if (data.data) {
10346         data = data.data;
10347     }
10348     Roo.data.MemoryProxy.superclass.constructor.call(this);
10349     this.data = data;
10350 };
10351
10352 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10353     /**
10354      * Load data from the requested source (in this case an in-memory
10355      * data object passed to the constructor), read the data object into
10356      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10357      * process that block using the passed callback.
10358      * @param {Object} params This parameter is not used by the MemoryProxy class.
10359      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10360      * object into a block of Roo.data.Records.
10361      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10362      * The function must be passed <ul>
10363      * <li>The Record block object</li>
10364      * <li>The "arg" argument from the load function</li>
10365      * <li>A boolean success indicator</li>
10366      * </ul>
10367      * @param {Object} scope The scope in which to call the callback
10368      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10369      */
10370     load : function(params, reader, callback, scope, arg){
10371         params = params || {};
10372         var result;
10373         try {
10374             result = reader.readRecords(this.data);
10375         }catch(e){
10376             this.fireEvent("loadexception", this, arg, null, e);
10377             callback.call(scope, null, arg, false);
10378             return;
10379         }
10380         callback.call(scope, result, arg, true);
10381     },
10382     
10383     // private
10384     update : function(params, records){
10385         
10386     }
10387 });/*
10388  * Based on:
10389  * Ext JS Library 1.1.1
10390  * Copyright(c) 2006-2007, Ext JS, LLC.
10391  *
10392  * Originally Released Under LGPL - original licence link has changed is not relivant.
10393  *
10394  * Fork - LGPL
10395  * <script type="text/javascript">
10396  */
10397 /**
10398  * @class Roo.data.HttpProxy
10399  * @extends Roo.data.DataProxy
10400  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10401  * configured to reference a certain URL.<br><br>
10402  * <p>
10403  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10404  * from which the running page was served.<br><br>
10405  * <p>
10406  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10407  * <p>
10408  * Be aware that to enable the browser to parse an XML document, the server must set
10409  * the Content-Type header in the HTTP response to "text/xml".
10410  * @constructor
10411  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10412  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10413  * will be used to make the request.
10414  */
10415 Roo.data.HttpProxy = function(conn){
10416     Roo.data.HttpProxy.superclass.constructor.call(this);
10417     // is conn a conn config or a real conn?
10418     this.conn = conn;
10419     this.useAjax = !conn || !conn.events;
10420   
10421 };
10422
10423 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10424     // thse are take from connection...
10425     
10426     /**
10427      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10428      */
10429     /**
10430      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10431      * extra parameters to each request made by this object. (defaults to undefined)
10432      */
10433     /**
10434      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10435      *  to each request made by this object. (defaults to undefined)
10436      */
10437     /**
10438      * @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)
10439      */
10440     /**
10441      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10442      */
10443      /**
10444      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10445      * @type Boolean
10446      */
10447   
10448
10449     /**
10450      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10451      * @type Boolean
10452      */
10453     /**
10454      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10455      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10456      * a finer-grained basis than the DataProxy events.
10457      */
10458     getConnection : function(){
10459         return this.useAjax ? Roo.Ajax : this.conn;
10460     },
10461
10462     /**
10463      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10464      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10465      * process that block using the passed callback.
10466      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10467      * for the request to the remote server.
10468      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10469      * object into a block of Roo.data.Records.
10470      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10471      * The function must be passed <ul>
10472      * <li>The Record block object</li>
10473      * <li>The "arg" argument from the load function</li>
10474      * <li>A boolean success indicator</li>
10475      * </ul>
10476      * @param {Object} scope The scope in which to call the callback
10477      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10478      */
10479     load : function(params, reader, callback, scope, arg){
10480         if(this.fireEvent("beforeload", this, params) !== false){
10481             var  o = {
10482                 params : params || {},
10483                 request: {
10484                     callback : callback,
10485                     scope : scope,
10486                     arg : arg
10487                 },
10488                 reader: reader,
10489                 callback : this.loadResponse,
10490                 scope: this
10491             };
10492             if(this.useAjax){
10493                 Roo.applyIf(o, this.conn);
10494                 if(this.activeRequest){
10495                     Roo.Ajax.abort(this.activeRequest);
10496                 }
10497                 this.activeRequest = Roo.Ajax.request(o);
10498             }else{
10499                 this.conn.request(o);
10500             }
10501         }else{
10502             callback.call(scope||this, null, arg, false);
10503         }
10504     },
10505
10506     // private
10507     loadResponse : function(o, success, response){
10508         delete this.activeRequest;
10509         if(!success){
10510             this.fireEvent("loadexception", this, o, response);
10511             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10512             return;
10513         }
10514         var result;
10515         try {
10516             result = o.reader.read(response);
10517         }catch(e){
10518             this.fireEvent("loadexception", this, o, response, e);
10519             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10520             return;
10521         }
10522         
10523         this.fireEvent("load", this, o, o.request.arg);
10524         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10525     },
10526
10527     // private
10528     update : function(dataSet){
10529
10530     },
10531
10532     // private
10533     updateResponse : function(dataSet){
10534
10535     }
10536 });/*
10537  * Based on:
10538  * Ext JS Library 1.1.1
10539  * Copyright(c) 2006-2007, Ext JS, LLC.
10540  *
10541  * Originally Released Under LGPL - original licence link has changed is not relivant.
10542  *
10543  * Fork - LGPL
10544  * <script type="text/javascript">
10545  */
10546
10547 /**
10548  * @class Roo.data.ScriptTagProxy
10549  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10550  * other than the originating domain of the running page.<br><br>
10551  * <p>
10552  * <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
10553  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10554  * <p>
10555  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10556  * source code that is used as the source inside a &lt;script> tag.<br><br>
10557  * <p>
10558  * In order for the browser to process the returned data, the server must wrap the data object
10559  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10560  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10561  * depending on whether the callback name was passed:
10562  * <p>
10563  * <pre><code>
10564 boolean scriptTag = false;
10565 String cb = request.getParameter("callback");
10566 if (cb != null) {
10567     scriptTag = true;
10568     response.setContentType("text/javascript");
10569 } else {
10570     response.setContentType("application/x-json");
10571 }
10572 Writer out = response.getWriter();
10573 if (scriptTag) {
10574     out.write(cb + "(");
10575 }
10576 out.print(dataBlock.toJsonString());
10577 if (scriptTag) {
10578     out.write(");");
10579 }
10580 </pre></code>
10581  *
10582  * @constructor
10583  * @param {Object} config A configuration object.
10584  */
10585 Roo.data.ScriptTagProxy = function(config){
10586     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10587     Roo.apply(this, config);
10588     this.head = document.getElementsByTagName("head")[0];
10589 };
10590
10591 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10592
10593 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10594     /**
10595      * @cfg {String} url The URL from which to request the data object.
10596      */
10597     /**
10598      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10599      */
10600     timeout : 30000,
10601     /**
10602      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10603      * the server the name of the callback function set up by the load call to process the returned data object.
10604      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10605      * javascript output which calls this named function passing the data object as its only parameter.
10606      */
10607     callbackParam : "callback",
10608     /**
10609      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10610      * name to the request.
10611      */
10612     nocache : true,
10613
10614     /**
10615      * Load data from the configured URL, read the data object into
10616      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10617      * process that block using the passed callback.
10618      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10619      * for the request to the remote server.
10620      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10621      * object into a block of Roo.data.Records.
10622      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10623      * The function must be passed <ul>
10624      * <li>The Record block object</li>
10625      * <li>The "arg" argument from the load function</li>
10626      * <li>A boolean success indicator</li>
10627      * </ul>
10628      * @param {Object} scope The scope in which to call the callback
10629      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10630      */
10631     load : function(params, reader, callback, scope, arg){
10632         if(this.fireEvent("beforeload", this, params) !== false){
10633
10634             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10635
10636             var url = this.url;
10637             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10638             if(this.nocache){
10639                 url += "&_dc=" + (new Date().getTime());
10640             }
10641             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10642             var trans = {
10643                 id : transId,
10644                 cb : "stcCallback"+transId,
10645                 scriptId : "stcScript"+transId,
10646                 params : params,
10647                 arg : arg,
10648                 url : url,
10649                 callback : callback,
10650                 scope : scope,
10651                 reader : reader
10652             };
10653             var conn = this;
10654
10655             window[trans.cb] = function(o){
10656                 conn.handleResponse(o, trans);
10657             };
10658
10659             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10660
10661             if(this.autoAbort !== false){
10662                 this.abort();
10663             }
10664
10665             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10666
10667             var script = document.createElement("script");
10668             script.setAttribute("src", url);
10669             script.setAttribute("type", "text/javascript");
10670             script.setAttribute("id", trans.scriptId);
10671             this.head.appendChild(script);
10672
10673             this.trans = trans;
10674         }else{
10675             callback.call(scope||this, null, arg, false);
10676         }
10677     },
10678
10679     // private
10680     isLoading : function(){
10681         return this.trans ? true : false;
10682     },
10683
10684     /**
10685      * Abort the current server request.
10686      */
10687     abort : function(){
10688         if(this.isLoading()){
10689             this.destroyTrans(this.trans);
10690         }
10691     },
10692
10693     // private
10694     destroyTrans : function(trans, isLoaded){
10695         this.head.removeChild(document.getElementById(trans.scriptId));
10696         clearTimeout(trans.timeoutId);
10697         if(isLoaded){
10698             window[trans.cb] = undefined;
10699             try{
10700                 delete window[trans.cb];
10701             }catch(e){}
10702         }else{
10703             // if hasn't been loaded, wait for load to remove it to prevent script error
10704             window[trans.cb] = function(){
10705                 window[trans.cb] = undefined;
10706                 try{
10707                     delete window[trans.cb];
10708                 }catch(e){}
10709             };
10710         }
10711     },
10712
10713     // private
10714     handleResponse : function(o, trans){
10715         this.trans = false;
10716         this.destroyTrans(trans, true);
10717         var result;
10718         try {
10719             result = trans.reader.readRecords(o);
10720         }catch(e){
10721             this.fireEvent("loadexception", this, o, trans.arg, e);
10722             trans.callback.call(trans.scope||window, null, trans.arg, false);
10723             return;
10724         }
10725         this.fireEvent("load", this, o, trans.arg);
10726         trans.callback.call(trans.scope||window, result, trans.arg, true);
10727     },
10728
10729     // private
10730     handleFailure : function(trans){
10731         this.trans = false;
10732         this.destroyTrans(trans, false);
10733         this.fireEvent("loadexception", this, null, trans.arg);
10734         trans.callback.call(trans.scope||window, null, trans.arg, false);
10735     }
10736 });/*
10737  * Based on:
10738  * Ext JS Library 1.1.1
10739  * Copyright(c) 2006-2007, Ext JS, LLC.
10740  *
10741  * Originally Released Under LGPL - original licence link has changed is not relivant.
10742  *
10743  * Fork - LGPL
10744  * <script type="text/javascript">
10745  */
10746
10747 /**
10748  * @class Roo.data.JsonReader
10749  * @extends Roo.data.DataReader
10750  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10751  * based on mappings in a provided Roo.data.Record constructor.
10752  * 
10753  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10754  * in the reply previously. 
10755  * 
10756  * <p>
10757  * Example code:
10758  * <pre><code>
10759 var RecordDef = Roo.data.Record.create([
10760     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10761     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10762 ]);
10763 var myReader = new Roo.data.JsonReader({
10764     totalProperty: "results",    // The property which contains the total dataset size (optional)
10765     root: "rows",                // The property which contains an Array of row objects
10766     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10767 }, RecordDef);
10768 </code></pre>
10769  * <p>
10770  * This would consume a JSON file like this:
10771  * <pre><code>
10772 { 'results': 2, 'rows': [
10773     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10774     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10775 }
10776 </code></pre>
10777  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10778  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10779  * paged from the remote server.
10780  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10781  * @cfg {String} root name of the property which contains the Array of row objects.
10782  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10783  * @cfg {Array} fields Array of field definition objects
10784  * @constructor
10785  * Create a new JsonReader
10786  * @param {Object} meta Metadata configuration options
10787  * @param {Object} recordType Either an Array of field definition objects,
10788  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10789  */
10790 Roo.data.JsonReader = function(meta, recordType){
10791     
10792     meta = meta || {};
10793     // set some defaults:
10794     Roo.applyIf(meta, {
10795         totalProperty: 'total',
10796         successProperty : 'success',
10797         root : 'data',
10798         id : 'id'
10799     });
10800     
10801     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10802 };
10803 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10804     
10805     /**
10806      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10807      * Used by Store query builder to append _requestMeta to params.
10808      * 
10809      */
10810     metaFromRemote : false,
10811     /**
10812      * This method is only used by a DataProxy which has retrieved data from a remote server.
10813      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10814      * @return {Object} data A data block which is used by an Roo.data.Store object as
10815      * a cache of Roo.data.Records.
10816      */
10817     read : function(response){
10818         var json = response.responseText;
10819        
10820         var o = /* eval:var:o */ eval("("+json+")");
10821         if(!o) {
10822             throw {message: "JsonReader.read: Json object not found"};
10823         }
10824         
10825         if(o.metaData){
10826             
10827             delete this.ef;
10828             this.metaFromRemote = true;
10829             this.meta = o.metaData;
10830             this.recordType = Roo.data.Record.create(o.metaData.fields);
10831             this.onMetaChange(this.meta, this.recordType, o);
10832         }
10833         return this.readRecords(o);
10834     },
10835
10836     // private function a store will implement
10837     onMetaChange : function(meta, recordType, o){
10838
10839     },
10840
10841     /**
10842          * @ignore
10843          */
10844     simpleAccess: function(obj, subsc) {
10845         return obj[subsc];
10846     },
10847
10848         /**
10849          * @ignore
10850          */
10851     getJsonAccessor: function(){
10852         var re = /[\[\.]/;
10853         return function(expr) {
10854             try {
10855                 return(re.test(expr))
10856                     ? new Function("obj", "return obj." + expr)
10857                     : function(obj){
10858                         return obj[expr];
10859                     };
10860             } catch(e){}
10861             return Roo.emptyFn;
10862         };
10863     }(),
10864
10865     /**
10866      * Create a data block containing Roo.data.Records from an XML document.
10867      * @param {Object} o An object which contains an Array of row objects in the property specified
10868      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10869      * which contains the total size of the dataset.
10870      * @return {Object} data A data block which is used by an Roo.data.Store object as
10871      * a cache of Roo.data.Records.
10872      */
10873     readRecords : function(o){
10874         /**
10875          * After any data loads, the raw JSON data is available for further custom processing.
10876          * @type Object
10877          */
10878         this.o = o;
10879         var s = this.meta, Record = this.recordType,
10880             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10881
10882 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10883         if (!this.ef) {
10884             if(s.totalProperty) {
10885                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10886                 }
10887                 if(s.successProperty) {
10888                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10889                 }
10890                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10891                 if (s.id) {
10892                         var g = this.getJsonAccessor(s.id);
10893                         this.getId = function(rec) {
10894                                 var r = g(rec);  
10895                                 return (r === undefined || r === "") ? null : r;
10896                         };
10897                 } else {
10898                         this.getId = function(){return null;};
10899                 }
10900             this.ef = [];
10901             for(var jj = 0; jj < fl; jj++){
10902                 f = fi[jj];
10903                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10904                 this.ef[jj] = this.getJsonAccessor(map);
10905             }
10906         }
10907
10908         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10909         if(s.totalProperty){
10910             var vt = parseInt(this.getTotal(o), 10);
10911             if(!isNaN(vt)){
10912                 totalRecords = vt;
10913             }
10914         }
10915         if(s.successProperty){
10916             var vs = this.getSuccess(o);
10917             if(vs === false || vs === 'false'){
10918                 success = false;
10919             }
10920         }
10921         var records = [];
10922         for(var i = 0; i < c; i++){
10923                 var n = root[i];
10924             var values = {};
10925             var id = this.getId(n);
10926             for(var j = 0; j < fl; j++){
10927                 f = fi[j];
10928             var v = this.ef[j](n);
10929             if (!f.convert) {
10930                 Roo.log('missing convert for ' + f.name);
10931                 Roo.log(f);
10932                 continue;
10933             }
10934             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10935             }
10936             var record = new Record(values, id);
10937             record.json = n;
10938             records[i] = record;
10939         }
10940         return {
10941             raw : o,
10942             success : success,
10943             records : records,
10944             totalRecords : totalRecords
10945         };
10946     }
10947 });/*
10948  * Based on:
10949  * Ext JS Library 1.1.1
10950  * Copyright(c) 2006-2007, Ext JS, LLC.
10951  *
10952  * Originally Released Under LGPL - original licence link has changed is not relivant.
10953  *
10954  * Fork - LGPL
10955  * <script type="text/javascript">
10956  */
10957
10958 /**
10959  * @class Roo.data.ArrayReader
10960  * @extends Roo.data.DataReader
10961  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10962  * Each element of that Array represents a row of data fields. The
10963  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10964  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10965  * <p>
10966  * Example code:.
10967  * <pre><code>
10968 var RecordDef = Roo.data.Record.create([
10969     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10970     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10971 ]);
10972 var myReader = new Roo.data.ArrayReader({
10973     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10974 }, RecordDef);
10975 </code></pre>
10976  * <p>
10977  * This would consume an Array like this:
10978  * <pre><code>
10979 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10980   </code></pre>
10981  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10982  * @constructor
10983  * Create a new JsonReader
10984  * @param {Object} meta Metadata configuration options.
10985  * @param {Object} recordType Either an Array of field definition objects
10986  * as specified to {@link Roo.data.Record#create},
10987  * or an {@link Roo.data.Record} object
10988  * created using {@link Roo.data.Record#create}.
10989  */
10990 Roo.data.ArrayReader = function(meta, recordType){
10991     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10992 };
10993
10994 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10995     /**
10996      * Create a data block containing Roo.data.Records from an XML document.
10997      * @param {Object} o An Array of row objects which represents the dataset.
10998      * @return {Object} data A data block which is used by an Roo.data.Store object as
10999      * a cache of Roo.data.Records.
11000      */
11001     readRecords : function(o){
11002         var sid = this.meta ? this.meta.id : null;
11003         var recordType = this.recordType, fields = recordType.prototype.fields;
11004         var records = [];
11005         var root = o;
11006             for(var i = 0; i < root.length; i++){
11007                     var n = root[i];
11008                 var values = {};
11009                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11010                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11011                 var f = fields.items[j];
11012                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11013                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11014                 v = f.convert(v);
11015                 values[f.name] = v;
11016             }
11017                 var record = new recordType(values, id);
11018                 record.json = n;
11019                 records[records.length] = record;
11020             }
11021             return {
11022                 records : records,
11023                 totalRecords : records.length
11024             };
11025     }
11026 });/*
11027  * - LGPL
11028  * * 
11029  */
11030
11031 /**
11032  * @class Roo.bootstrap.ComboBox
11033  * @extends Roo.bootstrap.TriggerField
11034  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11035  * @cfg {Boolean} append (true|false) default false
11036  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11037  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11038  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11039  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11040  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11041  * @cfg {Boolean} animate default true
11042  * @cfg {Boolean} emptyResultText only for touch device
11043  * @constructor
11044  * Create a new ComboBox.
11045  * @param {Object} config Configuration options
11046  */
11047 Roo.bootstrap.ComboBox = function(config){
11048     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11049     this.addEvents({
11050         /**
11051          * @event expand
11052          * Fires when the dropdown list is expanded
11053              * @param {Roo.bootstrap.ComboBox} combo This combo box
11054              */
11055         'expand' : true,
11056         /**
11057          * @event collapse
11058          * Fires when the dropdown list is collapsed
11059              * @param {Roo.bootstrap.ComboBox} combo This combo box
11060              */
11061         'collapse' : true,
11062         /**
11063          * @event beforeselect
11064          * Fires before a list item is selected. Return false to cancel the selection.
11065              * @param {Roo.bootstrap.ComboBox} combo This combo box
11066              * @param {Roo.data.Record} record The data record returned from the underlying store
11067              * @param {Number} index The index of the selected item in the dropdown list
11068              */
11069         'beforeselect' : true,
11070         /**
11071          * @event select
11072          * Fires when a list item is selected
11073              * @param {Roo.bootstrap.ComboBox} combo This combo box
11074              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11075              * @param {Number} index The index of the selected item in the dropdown list
11076              */
11077         'select' : true,
11078         /**
11079          * @event beforequery
11080          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11081          * The event object passed has these properties:
11082              * @param {Roo.bootstrap.ComboBox} combo This combo box
11083              * @param {String} query The query
11084              * @param {Boolean} forceAll true to force "all" query
11085              * @param {Boolean} cancel true to cancel the query
11086              * @param {Object} e The query event object
11087              */
11088         'beforequery': true,
11089          /**
11090          * @event add
11091          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11092              * @param {Roo.bootstrap.ComboBox} combo This combo box
11093              */
11094         'add' : true,
11095         /**
11096          * @event edit
11097          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11098              * @param {Roo.bootstrap.ComboBox} combo This combo box
11099              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11100              */
11101         'edit' : true,
11102         /**
11103          * @event remove
11104          * Fires when the remove value from the combobox array
11105              * @param {Roo.bootstrap.ComboBox} combo This combo box
11106              */
11107         'remove' : true,
11108         /**
11109          * @event specialfilter
11110          * Fires when specialfilter
11111             * @param {Roo.bootstrap.ComboBox} combo This combo box
11112             */
11113         'specialfilter' : true
11114         
11115     });
11116     
11117     this.item = [];
11118     this.tickItems = [];
11119     
11120     this.selectedIndex = -1;
11121     if(this.mode == 'local'){
11122         if(config.queryDelay === undefined){
11123             this.queryDelay = 10;
11124         }
11125         if(config.minChars === undefined){
11126             this.minChars = 0;
11127         }
11128     }
11129 };
11130
11131 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11132      
11133     /**
11134      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11135      * rendering into an Roo.Editor, defaults to false)
11136      */
11137     /**
11138      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11139      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11140      */
11141     /**
11142      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11143      */
11144     /**
11145      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11146      * the dropdown list (defaults to undefined, with no header element)
11147      */
11148
11149      /**
11150      * @cfg {String/Roo.Template} tpl The template to use to render the output
11151      */
11152      
11153      /**
11154      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11155      */
11156     listWidth: undefined,
11157     /**
11158      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11159      * mode = 'remote' or 'text' if mode = 'local')
11160      */
11161     displayField: undefined,
11162     
11163     /**
11164      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11165      * mode = 'remote' or 'value' if mode = 'local'). 
11166      * Note: use of a valueField requires the user make a selection
11167      * in order for a value to be mapped.
11168      */
11169     valueField: undefined,
11170     
11171     
11172     /**
11173      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11174      * field's data value (defaults to the underlying DOM element's name)
11175      */
11176     hiddenName: undefined,
11177     /**
11178      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11179      */
11180     listClass: '',
11181     /**
11182      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11183      */
11184     selectedClass: 'active',
11185     
11186     /**
11187      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11188      */
11189     shadow:'sides',
11190     /**
11191      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11192      * anchor positions (defaults to 'tl-bl')
11193      */
11194     listAlign: 'tl-bl?',
11195     /**
11196      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11197      */
11198     maxHeight: 300,
11199     /**
11200      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11201      * query specified by the allQuery config option (defaults to 'query')
11202      */
11203     triggerAction: 'query',
11204     /**
11205      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11206      * (defaults to 4, does not apply if editable = false)
11207      */
11208     minChars : 4,
11209     /**
11210      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11211      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11212      */
11213     typeAhead: false,
11214     /**
11215      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11216      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11217      */
11218     queryDelay: 500,
11219     /**
11220      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11221      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11222      */
11223     pageSize: 0,
11224     /**
11225      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11226      * when editable = true (defaults to false)
11227      */
11228     selectOnFocus:false,
11229     /**
11230      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11231      */
11232     queryParam: 'query',
11233     /**
11234      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11235      * when mode = 'remote' (defaults to 'Loading...')
11236      */
11237     loadingText: 'Loading...',
11238     /**
11239      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11240      */
11241     resizable: false,
11242     /**
11243      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11244      */
11245     handleHeight : 8,
11246     /**
11247      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11248      * traditional select (defaults to true)
11249      */
11250     editable: true,
11251     /**
11252      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11253      */
11254     allQuery: '',
11255     /**
11256      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11257      */
11258     mode: 'remote',
11259     /**
11260      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11261      * listWidth has a higher value)
11262      */
11263     minListWidth : 70,
11264     /**
11265      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11266      * allow the user to set arbitrary text into the field (defaults to false)
11267      */
11268     forceSelection:false,
11269     /**
11270      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11271      * if typeAhead = true (defaults to 250)
11272      */
11273     typeAheadDelay : 250,
11274     /**
11275      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11276      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11277      */
11278     valueNotFoundText : undefined,
11279     /**
11280      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11281      */
11282     blockFocus : false,
11283     
11284     /**
11285      * @cfg {Boolean} disableClear Disable showing of clear button.
11286      */
11287     disableClear : false,
11288     /**
11289      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11290      */
11291     alwaysQuery : false,
11292     
11293     /**
11294      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11295      */
11296     multiple : false,
11297     
11298     /**
11299      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11300      */
11301     invalidClass : "has-warning",
11302     
11303     /**
11304      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11305      */
11306     validClass : "has-success",
11307     
11308     /**
11309      * @cfg {Boolean} specialFilter (true|false) special filter default false
11310      */
11311     specialFilter : false,
11312     
11313     //private
11314     addicon : false,
11315     editicon: false,
11316     
11317     page: 0,
11318     hasQuery: false,
11319     append: false,
11320     loadNext: false,
11321     autoFocus : true,
11322     tickable : false,
11323     btnPosition : 'right',
11324     triggerList : true,
11325     showToggleBtn : true,
11326     animate : true,
11327     emptyResultText: 'Empty',
11328     // element that contains real text value.. (when hidden is used..)
11329     
11330     getAutoCreate : function()
11331     {
11332         var cfg = false;
11333         
11334         /*
11335          * Touch Devices
11336          */
11337         
11338         if(Roo.isTouch){
11339             cfg = this.getAutoCreateTouchView();
11340             return cfg;;
11341         }
11342         
11343         /*
11344          *  Normal ComboBox
11345          */
11346         if(!this.tickable){
11347             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11348             return cfg;
11349         }
11350         
11351         /*
11352          *  ComboBox with tickable selections
11353          */
11354              
11355         var align = this.labelAlign || this.parentLabelAlign();
11356         
11357         cfg = {
11358             cls : 'form-group roo-combobox-tickable' //input-group
11359         };
11360         
11361         var buttons = {
11362             tag : 'div',
11363             cls : 'tickable-buttons',
11364             cn : [
11365                 {
11366                     tag : 'button',
11367                     type : 'button',
11368                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11369                     html : 'Edit'
11370                 },
11371                 {
11372                     tag : 'button',
11373                     type : 'button',
11374                     name : 'ok',
11375                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11376                     html : 'Done'
11377                 },
11378                 {
11379                     tag : 'button',
11380                     type : 'button',
11381                     name : 'cancel',
11382                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11383                     html : 'Cancel'
11384                 }
11385             ]
11386         };
11387         
11388         if(this.editable){
11389             buttons.cn.unshift({
11390                 tag: 'input',
11391                 cls: 'select2-search-field-input'
11392             });
11393         }
11394         
11395         var _this = this;
11396         
11397         Roo.each(buttons.cn, function(c){
11398             if (_this.size) {
11399                 c.cls += ' btn-' + _this.size;
11400             }
11401
11402             if (_this.disabled) {
11403                 c.disabled = true;
11404             }
11405         });
11406         
11407         var box = {
11408             tag: 'div',
11409             cn: [
11410                 {
11411                     tag: 'input',
11412                     type : 'hidden',
11413                     cls: 'form-hidden-field'
11414                 },
11415                 {
11416                     tag: 'ul',
11417                     cls: 'select2-choices',
11418                     cn:[
11419                         {
11420                             tag: 'li',
11421                             cls: 'select2-search-field',
11422                             cn: [
11423
11424                                 buttons
11425                             ]
11426                         }
11427                     ]
11428                 }
11429             ]
11430         }
11431         
11432         var combobox = {
11433             cls: 'select2-container input-group select2-container-multi',
11434             cn: [
11435                 box
11436 //                {
11437 //                    tag: 'ul',
11438 //                    cls: 'typeahead typeahead-long dropdown-menu',
11439 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11440 //                }
11441             ]
11442         };
11443         
11444         if(this.hasFeedback && !this.allowBlank){
11445             
11446             var feedback = {
11447                 tag: 'span',
11448                 cls: 'glyphicon form-control-feedback'
11449             };
11450
11451             combobox.cn.push(feedback);
11452         }
11453         
11454         if (align ==='left' && this.fieldLabel.length) {
11455             
11456                 Roo.log("left and has label");
11457                 cfg.cn = [
11458                     
11459                     {
11460                         tag: 'label',
11461                         'for' :  id,
11462                         cls : 'control-label col-sm-' + this.labelWidth,
11463                         html : this.fieldLabel
11464                         
11465                     },
11466                     {
11467                         cls : "col-sm-" + (12 - this.labelWidth), 
11468                         cn: [
11469                             combobox
11470                         ]
11471                     }
11472                     
11473                 ];
11474         } else if ( this.fieldLabel.length) {
11475                 Roo.log(" label");
11476                  cfg.cn = [
11477                    
11478                     {
11479                         tag: 'label',
11480                         //cls : 'input-group-addon',
11481                         html : this.fieldLabel
11482                         
11483                     },
11484                     
11485                     combobox
11486                     
11487                 ];
11488
11489         } else {
11490             
11491                 Roo.log(" no label && no align");
11492                 cfg = combobox
11493                      
11494                 
11495         }
11496          
11497         var settings=this;
11498         ['xs','sm','md','lg'].map(function(size){
11499             if (settings[size]) {
11500                 cfg.cls += ' col-' + size + '-' + settings[size];
11501             }
11502         });
11503         
11504         return cfg;
11505         
11506     },
11507     
11508     // private
11509     initEvents: function()
11510     {
11511         
11512         if (!this.store) {
11513             throw "can not find store for combo";
11514         }
11515         
11516         this.store = Roo.factory(this.store, Roo.data);
11517         
11518         /*
11519          * Touch Devices
11520          */
11521         
11522         if(Roo.isTouch){
11523             this.initTouchView();
11524             return;
11525         }
11526         
11527         if(this.tickable){
11528             this.initTickableEvents();
11529             return;
11530         }
11531         
11532         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11533         
11534         if(this.hiddenName){
11535             
11536             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11537             
11538             this.hiddenField.dom.value =
11539                 this.hiddenValue !== undefined ? this.hiddenValue :
11540                 this.value !== undefined ? this.value : '';
11541
11542             // prevent input submission
11543             this.el.dom.removeAttribute('name');
11544             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11545              
11546              
11547         }
11548         //if(Roo.isGecko){
11549         //    this.el.dom.setAttribute('autocomplete', 'off');
11550         //}
11551         
11552         var cls = 'x-combo-list';
11553         
11554         //this.list = new Roo.Layer({
11555         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11556         //});
11557         
11558         var _this = this;
11559         
11560         (function(){
11561             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11562             _this.list.setWidth(lw);
11563         }).defer(100);
11564         
11565         this.list.on('mouseover', this.onViewOver, this);
11566         this.list.on('mousemove', this.onViewMove, this);
11567         
11568         this.list.on('scroll', this.onViewScroll, this);
11569         
11570         /*
11571         this.list.swallowEvent('mousewheel');
11572         this.assetHeight = 0;
11573
11574         if(this.title){
11575             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11576             this.assetHeight += this.header.getHeight();
11577         }
11578
11579         this.innerList = this.list.createChild({cls:cls+'-inner'});
11580         this.innerList.on('mouseover', this.onViewOver, this);
11581         this.innerList.on('mousemove', this.onViewMove, this);
11582         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11583         
11584         if(this.allowBlank && !this.pageSize && !this.disableClear){
11585             this.footer = this.list.createChild({cls:cls+'-ft'});
11586             this.pageTb = new Roo.Toolbar(this.footer);
11587            
11588         }
11589         if(this.pageSize){
11590             this.footer = this.list.createChild({cls:cls+'-ft'});
11591             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11592                     {pageSize: this.pageSize});
11593             
11594         }
11595         
11596         if (this.pageTb && this.allowBlank && !this.disableClear) {
11597             var _this = this;
11598             this.pageTb.add(new Roo.Toolbar.Fill(), {
11599                 cls: 'x-btn-icon x-btn-clear',
11600                 text: '&#160;',
11601                 handler: function()
11602                 {
11603                     _this.collapse();
11604                     _this.clearValue();
11605                     _this.onSelect(false, -1);
11606                 }
11607             });
11608         }
11609         if (this.footer) {
11610             this.assetHeight += this.footer.getHeight();
11611         }
11612         */
11613             
11614         if(!this.tpl){
11615             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11616         }
11617
11618         this.view = new Roo.View(this.list, this.tpl, {
11619             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11620         });
11621         //this.view.wrapEl.setDisplayed(false);
11622         this.view.on('click', this.onViewClick, this);
11623         
11624         
11625         
11626         this.store.on('beforeload', this.onBeforeLoad, this);
11627         this.store.on('load', this.onLoad, this);
11628         this.store.on('loadexception', this.onLoadException, this);
11629         /*
11630         if(this.resizable){
11631             this.resizer = new Roo.Resizable(this.list,  {
11632                pinned:true, handles:'se'
11633             });
11634             this.resizer.on('resize', function(r, w, h){
11635                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11636                 this.listWidth = w;
11637                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11638                 this.restrictHeight();
11639             }, this);
11640             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11641         }
11642         */
11643         if(!this.editable){
11644             this.editable = true;
11645             this.setEditable(false);
11646         }
11647         
11648         /*
11649         
11650         if (typeof(this.events.add.listeners) != 'undefined') {
11651             
11652             this.addicon = this.wrap.createChild(
11653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11654        
11655             this.addicon.on('click', function(e) {
11656                 this.fireEvent('add', this);
11657             }, this);
11658         }
11659         if (typeof(this.events.edit.listeners) != 'undefined') {
11660             
11661             this.editicon = this.wrap.createChild(
11662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11663             if (this.addicon) {
11664                 this.editicon.setStyle('margin-left', '40px');
11665             }
11666             this.editicon.on('click', function(e) {
11667                 
11668                 // we fire even  if inothing is selected..
11669                 this.fireEvent('edit', this, this.lastData );
11670                 
11671             }, this);
11672         }
11673         */
11674         
11675         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11676             "up" : function(e){
11677                 this.inKeyMode = true;
11678                 this.selectPrev();
11679             },
11680
11681             "down" : function(e){
11682                 if(!this.isExpanded()){
11683                     this.onTriggerClick();
11684                 }else{
11685                     this.inKeyMode = true;
11686                     this.selectNext();
11687                 }
11688             },
11689
11690             "enter" : function(e){
11691 //                this.onViewClick();
11692                 //return true;
11693                 this.collapse();
11694                 
11695                 if(this.fireEvent("specialkey", this, e)){
11696                     this.onViewClick(false);
11697                 }
11698                 
11699                 return true;
11700             },
11701
11702             "esc" : function(e){
11703                 this.collapse();
11704             },
11705
11706             "tab" : function(e){
11707                 this.collapse();
11708                 
11709                 if(this.fireEvent("specialkey", this, e)){
11710                     this.onViewClick(false);
11711                 }
11712                 
11713                 return true;
11714             },
11715
11716             scope : this,
11717
11718             doRelay : function(foo, bar, hname){
11719                 if(hname == 'down' || this.scope.isExpanded()){
11720                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11721                 }
11722                 return true;
11723             },
11724
11725             forceKeyDown: true
11726         });
11727         
11728         
11729         this.queryDelay = Math.max(this.queryDelay || 10,
11730                 this.mode == 'local' ? 10 : 250);
11731         
11732         
11733         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11734         
11735         if(this.typeAhead){
11736             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11737         }
11738         if(this.editable !== false){
11739             this.inputEl().on("keyup", this.onKeyUp, this);
11740         }
11741         if(this.forceSelection){
11742             this.inputEl().on('blur', this.doForce, this);
11743         }
11744         
11745         if(this.multiple){
11746             this.choices = this.el.select('ul.select2-choices', true).first();
11747             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11748         }
11749     },
11750     
11751     initTickableEvents: function()
11752     {   
11753         this.createList();
11754         
11755         if(this.hiddenName){
11756             
11757             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11758             
11759             this.hiddenField.dom.value =
11760                 this.hiddenValue !== undefined ? this.hiddenValue :
11761                 this.value !== undefined ? this.value : '';
11762
11763             // prevent input submission
11764             this.el.dom.removeAttribute('name');
11765             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11766              
11767              
11768         }
11769         
11770 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11771         
11772         this.choices = this.el.select('ul.select2-choices', true).first();
11773         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11774         if(this.triggerList){
11775             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11776         }
11777          
11778         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11779         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11780         
11781         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11782         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11783         
11784         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11785         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11786         
11787         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11788         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11789         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11790         
11791         this.okBtn.hide();
11792         this.cancelBtn.hide();
11793         
11794         var _this = this;
11795         
11796         (function(){
11797             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11798             _this.list.setWidth(lw);
11799         }).defer(100);
11800         
11801         this.list.on('mouseover', this.onViewOver, this);
11802         this.list.on('mousemove', this.onViewMove, this);
11803         
11804         this.list.on('scroll', this.onViewScroll, this);
11805         
11806         if(!this.tpl){
11807             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>';
11808         }
11809
11810         this.view = new Roo.View(this.list, this.tpl, {
11811             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11812         });
11813         
11814         //this.view.wrapEl.setDisplayed(false);
11815         this.view.on('click', this.onViewClick, this);
11816         
11817         
11818         
11819         this.store.on('beforeload', this.onBeforeLoad, this);
11820         this.store.on('load', this.onLoad, this);
11821         this.store.on('loadexception', this.onLoadException, this);
11822         
11823         if(this.editable){
11824             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11825                 "up" : function(e){
11826                     this.inKeyMode = true;
11827                     this.selectPrev();
11828                 },
11829
11830                 "down" : function(e){
11831                     this.inKeyMode = true;
11832                     this.selectNext();
11833                 },
11834
11835                 "enter" : function(e){
11836                     if(this.fireEvent("specialkey", this, e)){
11837                         this.onViewClick(false);
11838                     }
11839                     
11840                     return true;
11841                 },
11842
11843                 "esc" : function(e){
11844                     this.onTickableFooterButtonClick(e, false, false);
11845                 },
11846
11847                 "tab" : function(e){
11848                     this.fireEvent("specialkey", this, e);
11849                     
11850                     this.onTickableFooterButtonClick(e, false, false);
11851                     
11852                     return true;
11853                 },
11854
11855                 scope : this,
11856
11857                 doRelay : function(e, fn, key){
11858                     if(this.scope.isExpanded()){
11859                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11860                     }
11861                     return true;
11862                 },
11863
11864                 forceKeyDown: true
11865             });
11866         }
11867         
11868         this.queryDelay = Math.max(this.queryDelay || 10,
11869                 this.mode == 'local' ? 10 : 250);
11870         
11871         
11872         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11873         
11874         if(this.typeAhead){
11875             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11876         }
11877         
11878         if(this.editable !== false){
11879             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11880         }
11881         
11882     },
11883
11884     onDestroy : function(){
11885         if(this.view){
11886             this.view.setStore(null);
11887             this.view.el.removeAllListeners();
11888             this.view.el.remove();
11889             this.view.purgeListeners();
11890         }
11891         if(this.list){
11892             this.list.dom.innerHTML  = '';
11893         }
11894         
11895         if(this.store){
11896             this.store.un('beforeload', this.onBeforeLoad, this);
11897             this.store.un('load', this.onLoad, this);
11898             this.store.un('loadexception', this.onLoadException, this);
11899         }
11900         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11901     },
11902
11903     // private
11904     fireKey : function(e){
11905         if(e.isNavKeyPress() && !this.list.isVisible()){
11906             this.fireEvent("specialkey", this, e);
11907         }
11908     },
11909
11910     // private
11911     onResize: function(w, h){
11912 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11913 //        
11914 //        if(typeof w != 'number'){
11915 //            // we do not handle it!?!?
11916 //            return;
11917 //        }
11918 //        var tw = this.trigger.getWidth();
11919 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11920 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11921 //        var x = w - tw;
11922 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11923 //            
11924 //        //this.trigger.setStyle('left', x+'px');
11925 //        
11926 //        if(this.list && this.listWidth === undefined){
11927 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11928 //            this.list.setWidth(lw);
11929 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11930 //        }
11931         
11932     
11933         
11934     },
11935
11936     /**
11937      * Allow or prevent the user from directly editing the field text.  If false is passed,
11938      * the user will only be able to select from the items defined in the dropdown list.  This method
11939      * is the runtime equivalent of setting the 'editable' config option at config time.
11940      * @param {Boolean} value True to allow the user to directly edit the field text
11941      */
11942     setEditable : function(value){
11943         if(value == this.editable){
11944             return;
11945         }
11946         this.editable = value;
11947         if(!value){
11948             this.inputEl().dom.setAttribute('readOnly', true);
11949             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11950             this.inputEl().addClass('x-combo-noedit');
11951         }else{
11952             this.inputEl().dom.setAttribute('readOnly', false);
11953             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11954             this.inputEl().removeClass('x-combo-noedit');
11955         }
11956     },
11957
11958     // private
11959     
11960     onBeforeLoad : function(combo,opts){
11961         if(!this.hasFocus){
11962             return;
11963         }
11964          if (!opts.add) {
11965             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11966          }
11967         this.restrictHeight();
11968         this.selectedIndex = -1;
11969     },
11970
11971     // private
11972     onLoad : function(){
11973         
11974         this.hasQuery = false;
11975         
11976         if(!this.hasFocus){
11977             return;
11978         }
11979         
11980         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11981             this.loading.hide();
11982         }
11983              
11984         if(this.store.getCount() > 0){
11985             this.expand();
11986             this.restrictHeight();
11987             if(this.lastQuery == this.allQuery){
11988                 if(this.editable && !this.tickable){
11989                     this.inputEl().dom.select();
11990                 }
11991                 
11992                 if(
11993                     !this.selectByValue(this.value, true) &&
11994                     this.autoFocus && 
11995                     (
11996                         !this.store.lastOptions ||
11997                         typeof(this.store.lastOptions.add) == 'undefined' || 
11998                         this.store.lastOptions.add != true
11999                     )
12000                 ){
12001                     this.select(0, true);
12002                 }
12003             }else{
12004                 if(this.autoFocus){
12005                     this.selectNext();
12006                 }
12007                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12008                     this.taTask.delay(this.typeAheadDelay);
12009                 }
12010             }
12011         }else{
12012             this.onEmptyResults();
12013         }
12014         
12015         //this.el.focus();
12016     },
12017     // private
12018     onLoadException : function()
12019     {
12020         this.hasQuery = false;
12021         
12022         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12023             this.loading.hide();
12024         }
12025         
12026         if(this.tickable && this.editable){
12027             return;
12028         }
12029         
12030         this.collapse();
12031         
12032         Roo.log(this.store.reader.jsonData);
12033         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12034             // fixme
12035             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12036         }
12037         
12038         
12039     },
12040     // private
12041     onTypeAhead : function(){
12042         if(this.store.getCount() > 0){
12043             var r = this.store.getAt(0);
12044             var newValue = r.data[this.displayField];
12045             var len = newValue.length;
12046             var selStart = this.getRawValue().length;
12047             
12048             if(selStart != len){
12049                 this.setRawValue(newValue);
12050                 this.selectText(selStart, newValue.length);
12051             }
12052         }
12053     },
12054
12055     // private
12056     onSelect : function(record, index){
12057         
12058         if(this.fireEvent('beforeselect', this, record, index) !== false){
12059         
12060             this.setFromData(index > -1 ? record.data : false);
12061             
12062             this.collapse();
12063             this.fireEvent('select', this, record, index);
12064         }
12065     },
12066
12067     /**
12068      * Returns the currently selected field value or empty string if no value is set.
12069      * @return {String} value The selected value
12070      */
12071     getValue : function(){
12072         
12073         if(this.multiple){
12074             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12075         }
12076         
12077         if(this.valueField){
12078             return typeof this.value != 'undefined' ? this.value : '';
12079         }else{
12080             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12081         }
12082     },
12083
12084     /**
12085      * Clears any text/value currently set in the field
12086      */
12087     clearValue : function(){
12088         if(this.hiddenField){
12089             this.hiddenField.dom.value = '';
12090         }
12091         this.value = '';
12092         this.setRawValue('');
12093         this.lastSelectionText = '';
12094         this.lastData = false;
12095         
12096         var close = this.closeTriggerEl();
12097         
12098         if(close){
12099             close.hide();
12100         }
12101         
12102     },
12103
12104     /**
12105      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12106      * will be displayed in the field.  If the value does not match the data value of an existing item,
12107      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12108      * Otherwise the field will be blank (although the value will still be set).
12109      * @param {String} value The value to match
12110      */
12111     setValue : function(v){
12112         if(this.multiple){
12113             this.syncValue();
12114             return;
12115         }
12116         
12117         var text = v;
12118         if(this.valueField){
12119             var r = this.findRecord(this.valueField, v);
12120             if(r){
12121                 text = r.data[this.displayField];
12122             }else if(this.valueNotFoundText !== undefined){
12123                 text = this.valueNotFoundText;
12124             }
12125         }
12126         this.lastSelectionText = text;
12127         if(this.hiddenField){
12128             this.hiddenField.dom.value = v;
12129         }
12130         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12131         this.value = v;
12132         
12133         var close = this.closeTriggerEl();
12134         
12135         if(close){
12136             (v.length || v * 1 > 0) ? close.show() : close.hide();
12137         }
12138     },
12139     /**
12140      * @property {Object} the last set data for the element
12141      */
12142     
12143     lastData : false,
12144     /**
12145      * Sets the value of the field based on a object which is related to the record format for the store.
12146      * @param {Object} value the value to set as. or false on reset?
12147      */
12148     setFromData : function(o){
12149         
12150         if(this.multiple){
12151             this.addItem(o);
12152             return;
12153         }
12154             
12155         var dv = ''; // display value
12156         var vv = ''; // value value..
12157         this.lastData = o;
12158         if (this.displayField) {
12159             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12160         } else {
12161             // this is an error condition!!!
12162             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12163         }
12164         
12165         if(this.valueField){
12166             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12167         }
12168         
12169         var close = this.closeTriggerEl();
12170         
12171         if(close){
12172             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12173         }
12174         
12175         if(this.hiddenField){
12176             this.hiddenField.dom.value = vv;
12177             
12178             this.lastSelectionText = dv;
12179             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12180             this.value = vv;
12181             return;
12182         }
12183         // no hidden field.. - we store the value in 'value', but still display
12184         // display field!!!!
12185         this.lastSelectionText = dv;
12186         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12187         this.value = vv;
12188         
12189         
12190         
12191     },
12192     // private
12193     reset : function(){
12194         // overridden so that last data is reset..
12195         
12196         if(this.multiple){
12197             this.clearItem();
12198             return;
12199         }
12200         
12201         this.setValue(this.originalValue);
12202         this.clearInvalid();
12203         this.lastData = false;
12204         if (this.view) {
12205             this.view.clearSelections();
12206         }
12207     },
12208     // private
12209     findRecord : function(prop, value){
12210         var record;
12211         if(this.store.getCount() > 0){
12212             this.store.each(function(r){
12213                 if(r.data[prop] == value){
12214                     record = r;
12215                     return false;
12216                 }
12217                 return true;
12218             });
12219         }
12220         return record;
12221     },
12222     
12223     getName: function()
12224     {
12225         // returns hidden if it's set..
12226         if (!this.rendered) {return ''};
12227         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12228         
12229     },
12230     // private
12231     onViewMove : function(e, t){
12232         this.inKeyMode = false;
12233     },
12234
12235     // private
12236     onViewOver : function(e, t){
12237         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12238             return;
12239         }
12240         var item = this.view.findItemFromChild(t);
12241         
12242         if(item){
12243             var index = this.view.indexOf(item);
12244             this.select(index, false);
12245         }
12246     },
12247
12248     // private
12249     onViewClick : function(view, doFocus, el, e)
12250     {
12251         var index = this.view.getSelectedIndexes()[0];
12252         
12253         var r = this.store.getAt(index);
12254         
12255         if(this.tickable){
12256             
12257             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12258                 return;
12259             }
12260             
12261             var rm = false;
12262             var _this = this;
12263             
12264             Roo.each(this.tickItems, function(v,k){
12265                 
12266                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12267                     _this.tickItems.splice(k, 1);
12268                     
12269                     if(typeof(e) == 'undefined' && view == false){
12270                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12271                     }
12272                     
12273                     rm = true;
12274                     return;
12275                 }
12276             });
12277             
12278             if(rm){
12279                 return;
12280             }
12281             
12282             this.tickItems.push(r.data);
12283             
12284             if(typeof(e) == 'undefined' && view == false){
12285                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12286             }
12287                     
12288             return;
12289         }
12290         
12291         if(r){
12292             this.onSelect(r, index);
12293         }
12294         if(doFocus !== false && !this.blockFocus){
12295             this.inputEl().focus();
12296         }
12297     },
12298
12299     // private
12300     restrictHeight : function(){
12301         //this.innerList.dom.style.height = '';
12302         //var inner = this.innerList.dom;
12303         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12304         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12305         //this.list.beginUpdate();
12306         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12307         this.list.alignTo(this.inputEl(), this.listAlign);
12308         this.list.alignTo(this.inputEl(), this.listAlign);
12309         //this.list.endUpdate();
12310     },
12311
12312     // private
12313     onEmptyResults : function(){
12314         
12315         if(this.tickable && this.editable){
12316             this.restrictHeight();
12317             return;
12318         }
12319         
12320         this.collapse();
12321     },
12322
12323     /**
12324      * Returns true if the dropdown list is expanded, else false.
12325      */
12326     isExpanded : function(){
12327         return this.list.isVisible();
12328     },
12329
12330     /**
12331      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12332      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12333      * @param {String} value The data value of the item to select
12334      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12335      * selected item if it is not currently in view (defaults to true)
12336      * @return {Boolean} True if the value matched an item in the list, else false
12337      */
12338     selectByValue : function(v, scrollIntoView){
12339         if(v !== undefined && v !== null){
12340             var r = this.findRecord(this.valueField || this.displayField, v);
12341             if(r){
12342                 this.select(this.store.indexOf(r), scrollIntoView);
12343                 return true;
12344             }
12345         }
12346         return false;
12347     },
12348
12349     /**
12350      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12351      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12352      * @param {Number} index The zero-based index of the list item to select
12353      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12354      * selected item if it is not currently in view (defaults to true)
12355      */
12356     select : function(index, scrollIntoView){
12357         this.selectedIndex = index;
12358         this.view.select(index);
12359         if(scrollIntoView !== false){
12360             var el = this.view.getNode(index);
12361             /*
12362              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12363              */
12364             if(el){
12365                 this.list.scrollChildIntoView(el, false);
12366             }
12367         }
12368     },
12369
12370     // private
12371     selectNext : function(){
12372         var ct = this.store.getCount();
12373         if(ct > 0){
12374             if(this.selectedIndex == -1){
12375                 this.select(0);
12376             }else if(this.selectedIndex < ct-1){
12377                 this.select(this.selectedIndex+1);
12378             }
12379         }
12380     },
12381
12382     // private
12383     selectPrev : function(){
12384         var ct = this.store.getCount();
12385         if(ct > 0){
12386             if(this.selectedIndex == -1){
12387                 this.select(0);
12388             }else if(this.selectedIndex != 0){
12389                 this.select(this.selectedIndex-1);
12390             }
12391         }
12392     },
12393
12394     // private
12395     onKeyUp : function(e){
12396         if(this.editable !== false && !e.isSpecialKey()){
12397             this.lastKey = e.getKey();
12398             this.dqTask.delay(this.queryDelay);
12399         }
12400     },
12401
12402     // private
12403     validateBlur : function(){
12404         return !this.list || !this.list.isVisible();   
12405     },
12406
12407     // private
12408     initQuery : function(){
12409         
12410         var v = this.getRawValue();
12411         
12412         if(this.tickable && this.editable){
12413             v = this.tickableInputEl().getValue();
12414         }
12415         
12416         this.doQuery(v);
12417     },
12418
12419     // private
12420     doForce : function(){
12421         if(this.inputEl().dom.value.length > 0){
12422             this.inputEl().dom.value =
12423                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12424              
12425         }
12426     },
12427
12428     /**
12429      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12430      * query allowing the query action to be canceled if needed.
12431      * @param {String} query The SQL query to execute
12432      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12433      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12434      * saved in the current store (defaults to false)
12435      */
12436     doQuery : function(q, forceAll){
12437         
12438         if(q === undefined || q === null){
12439             q = '';
12440         }
12441         var qe = {
12442             query: q,
12443             forceAll: forceAll,
12444             combo: this,
12445             cancel:false
12446         };
12447         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12448             return false;
12449         }
12450         q = qe.query;
12451         
12452         forceAll = qe.forceAll;
12453         if(forceAll === true || (q.length >= this.minChars)){
12454             
12455             this.hasQuery = true;
12456             
12457             if(this.lastQuery != q || this.alwaysQuery){
12458                 this.lastQuery = q;
12459                 if(this.mode == 'local'){
12460                     this.selectedIndex = -1;
12461                     if(forceAll){
12462                         this.store.clearFilter();
12463                     }else{
12464                         
12465                         if(this.specialFilter){
12466                             this.fireEvent('specialfilter', this);
12467                             this.onLoad();
12468                             return;
12469                         }
12470                         
12471                         this.store.filter(this.displayField, q);
12472                     }
12473                     
12474                     this.store.fireEvent("datachanged", this.store);
12475                     
12476                     this.onLoad();
12477                     
12478                     
12479                 }else{
12480                     
12481                     this.store.baseParams[this.queryParam] = q;
12482                     
12483                     var options = {params : this.getParams(q)};
12484                     
12485                     if(this.loadNext){
12486                         options.add = true;
12487                         options.params.start = this.page * this.pageSize;
12488                     }
12489                     
12490                     this.store.load(options);
12491                     
12492                     /*
12493                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12494                      *  we should expand the list on onLoad
12495                      *  so command out it
12496                      */
12497 //                    this.expand();
12498                 }
12499             }else{
12500                 this.selectedIndex = -1;
12501                 this.onLoad();   
12502             }
12503         }
12504         
12505         this.loadNext = false;
12506     },
12507     
12508     // private
12509     getParams : function(q){
12510         var p = {};
12511         //p[this.queryParam] = q;
12512         
12513         if(this.pageSize){
12514             p.start = 0;
12515             p.limit = this.pageSize;
12516         }
12517         return p;
12518     },
12519
12520     /**
12521      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12522      */
12523     collapse : function(){
12524         if(!this.isExpanded()){
12525             return;
12526         }
12527         
12528         this.list.hide();
12529         
12530         if(this.tickable){
12531             this.hasFocus = false;
12532             this.okBtn.hide();
12533             this.cancelBtn.hide();
12534             this.trigger.show();
12535             
12536             if(this.editable){
12537                 this.tickableInputEl().dom.value = '';
12538                 this.tickableInputEl().blur();
12539             }
12540             
12541         }
12542         
12543         Roo.get(document).un('mousedown', this.collapseIf, this);
12544         Roo.get(document).un('mousewheel', this.collapseIf, this);
12545         if (!this.editable) {
12546             Roo.get(document).un('keydown', this.listKeyPress, this);
12547         }
12548         this.fireEvent('collapse', this);
12549     },
12550
12551     // private
12552     collapseIf : function(e){
12553         var in_combo  = e.within(this.el);
12554         var in_list =  e.within(this.list);
12555         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12556         
12557         if (in_combo || in_list || is_list) {
12558             //e.stopPropagation();
12559             return;
12560         }
12561         
12562         if(this.tickable){
12563             this.onTickableFooterButtonClick(e, false, false);
12564         }
12565
12566         this.collapse();
12567         
12568     },
12569
12570     /**
12571      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12572      */
12573     expand : function(){
12574        
12575         if(this.isExpanded() || !this.hasFocus){
12576             return;
12577         }
12578         
12579         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12580         this.list.setWidth(lw);
12581         
12582         
12583          Roo.log('expand');
12584         
12585         this.list.show();
12586         
12587         this.restrictHeight();
12588         
12589         if(this.tickable){
12590             
12591             this.tickItems = Roo.apply([], this.item);
12592             
12593             this.okBtn.show();
12594             this.cancelBtn.show();
12595             this.trigger.hide();
12596             
12597             if(this.editable){
12598                 this.tickableInputEl().focus();
12599             }
12600             
12601         }
12602         
12603         Roo.get(document).on('mousedown', this.collapseIf, this);
12604         Roo.get(document).on('mousewheel', this.collapseIf, this);
12605         if (!this.editable) {
12606             Roo.get(document).on('keydown', this.listKeyPress, this);
12607         }
12608         
12609         this.fireEvent('expand', this);
12610     },
12611
12612     // private
12613     // Implements the default empty TriggerField.onTriggerClick function
12614     onTriggerClick : function(e)
12615     {
12616         Roo.log('trigger click');
12617         
12618         if(this.disabled || !this.triggerList){
12619             return;
12620         }
12621         
12622         this.page = 0;
12623         this.loadNext = false;
12624         
12625         if(this.isExpanded()){
12626             this.collapse();
12627             if (!this.blockFocus) {
12628                 this.inputEl().focus();
12629             }
12630             
12631         }else {
12632             this.hasFocus = true;
12633             if(this.triggerAction == 'all') {
12634                 this.doQuery(this.allQuery, true);
12635             } else {
12636                 this.doQuery(this.getRawValue());
12637             }
12638             if (!this.blockFocus) {
12639                 this.inputEl().focus();
12640             }
12641         }
12642     },
12643     
12644     onTickableTriggerClick : function(e)
12645     {
12646         if(this.disabled){
12647             return;
12648         }
12649         
12650         this.page = 0;
12651         this.loadNext = false;
12652         this.hasFocus = true;
12653         
12654         if(this.triggerAction == 'all') {
12655             this.doQuery(this.allQuery, true);
12656         } else {
12657             this.doQuery(this.getRawValue());
12658         }
12659     },
12660     
12661     onSearchFieldClick : function(e)
12662     {
12663         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12664             this.onTickableFooterButtonClick(e, false, false);
12665             return;
12666         }
12667         
12668         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12669             return;
12670         }
12671         
12672         this.page = 0;
12673         this.loadNext = false;
12674         this.hasFocus = true;
12675         
12676         if(this.triggerAction == 'all') {
12677             this.doQuery(this.allQuery, true);
12678         } else {
12679             this.doQuery(this.getRawValue());
12680         }
12681     },
12682     
12683     listKeyPress : function(e)
12684     {
12685         //Roo.log('listkeypress');
12686         // scroll to first matching element based on key pres..
12687         if (e.isSpecialKey()) {
12688             return false;
12689         }
12690         var k = String.fromCharCode(e.getKey()).toUpperCase();
12691         //Roo.log(k);
12692         var match  = false;
12693         var csel = this.view.getSelectedNodes();
12694         var cselitem = false;
12695         if (csel.length) {
12696             var ix = this.view.indexOf(csel[0]);
12697             cselitem  = this.store.getAt(ix);
12698             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12699                 cselitem = false;
12700             }
12701             
12702         }
12703         
12704         this.store.each(function(v) { 
12705             if (cselitem) {
12706                 // start at existing selection.
12707                 if (cselitem.id == v.id) {
12708                     cselitem = false;
12709                 }
12710                 return true;
12711             }
12712                 
12713             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12714                 match = this.store.indexOf(v);
12715                 return false;
12716             }
12717             return true;
12718         }, this);
12719         
12720         if (match === false) {
12721             return true; // no more action?
12722         }
12723         // scroll to?
12724         this.view.select(match);
12725         var sn = Roo.get(this.view.getSelectedNodes()[0])
12726         sn.scrollIntoView(sn.dom.parentNode, false);
12727     },
12728     
12729     onViewScroll : function(e, t){
12730         
12731         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){
12732             return;
12733         }
12734         
12735         this.hasQuery = true;
12736         
12737         this.loading = this.list.select('.loading', true).first();
12738         
12739         if(this.loading === null){
12740             this.list.createChild({
12741                 tag: 'div',
12742                 cls: 'loading select2-more-results select2-active',
12743                 html: 'Loading more results...'
12744             })
12745             
12746             this.loading = this.list.select('.loading', true).first();
12747             
12748             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12749             
12750             this.loading.hide();
12751         }
12752         
12753         this.loading.show();
12754         
12755         var _combo = this;
12756         
12757         this.page++;
12758         this.loadNext = true;
12759         
12760         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12761         
12762         return;
12763     },
12764     
12765     addItem : function(o)
12766     {   
12767         var dv = ''; // display value
12768         
12769         if (this.displayField) {
12770             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12771         } else {
12772             // this is an error condition!!!
12773             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12774         }
12775         
12776         if(!dv.length){
12777             return;
12778         }
12779         
12780         var choice = this.choices.createChild({
12781             tag: 'li',
12782             cls: 'select2-search-choice',
12783             cn: [
12784                 {
12785                     tag: 'div',
12786                     html: dv
12787                 },
12788                 {
12789                     tag: 'a',
12790                     href: '#',
12791                     cls: 'select2-search-choice-close',
12792                     tabindex: '-1'
12793                 }
12794             ]
12795             
12796         }, this.searchField);
12797         
12798         var close = choice.select('a.select2-search-choice-close', true).first()
12799         
12800         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12801         
12802         this.item.push(o);
12803         
12804         this.lastData = o;
12805         
12806         this.syncValue();
12807         
12808         this.inputEl().dom.value = '';
12809         
12810         this.validate();
12811     },
12812     
12813     onRemoveItem : function(e, _self, o)
12814     {
12815         e.preventDefault();
12816         
12817         this.lastItem = Roo.apply([], this.item);
12818         
12819         var index = this.item.indexOf(o.data) * 1;
12820         
12821         if( index < 0){
12822             Roo.log('not this item?!');
12823             return;
12824         }
12825         
12826         this.item.splice(index, 1);
12827         o.item.remove();
12828         
12829         this.syncValue();
12830         
12831         this.fireEvent('remove', this, e);
12832         
12833         this.validate();
12834         
12835     },
12836     
12837     syncValue : function()
12838     {
12839         if(!this.item.length){
12840             this.clearValue();
12841             return;
12842         }
12843             
12844         var value = [];
12845         var _this = this;
12846         Roo.each(this.item, function(i){
12847             if(_this.valueField){
12848                 value.push(i[_this.valueField]);
12849                 return;
12850             }
12851
12852             value.push(i);
12853         });
12854
12855         this.value = value.join(',');
12856
12857         if(this.hiddenField){
12858             this.hiddenField.dom.value = this.value;
12859         }
12860         
12861         this.store.fireEvent("datachanged", this.store);
12862     },
12863     
12864     clearItem : function()
12865     {
12866         if(!this.multiple){
12867             return;
12868         }
12869         
12870         this.item = [];
12871         
12872         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12873            c.remove();
12874         });
12875         
12876         this.syncValue();
12877         
12878         this.validate();
12879     },
12880     
12881     inputEl: function ()
12882     {
12883         if(this.tickable && !Roo.isTouch){
12884             return this.searchField;
12885         }
12886         return this.el.select('input.form-control',true).first();
12887     },
12888     
12889     
12890     onTickableFooterButtonClick : function(e, btn, el)
12891     {
12892         e.preventDefault();
12893         
12894         this.lastItem = Roo.apply([], this.item);
12895         
12896         if(btn && btn.name == 'cancel'){
12897             this.tickItems = Roo.apply([], this.item);
12898             this.collapse();
12899             return;
12900         }
12901         
12902         this.clearItem();
12903         
12904         var _this = this;
12905         
12906         Roo.each(this.tickItems, function(o){
12907             _this.addItem(o);
12908         });
12909         
12910         this.collapse();
12911         
12912     },
12913     
12914     validate : function()
12915     {
12916         var v = this.getRawValue();
12917         
12918         if(this.multiple){
12919             v = this.getValue();
12920         }
12921         
12922         if(this.disabled || this.allowBlank || v.length){
12923             this.markValid();
12924             return true;
12925         }
12926         
12927         this.markInvalid();
12928         return false;
12929     },
12930     
12931     tickableInputEl : function()
12932     {
12933         if(!this.tickable || !this.editable){
12934             return this.inputEl();
12935         }
12936         
12937         return this.inputEl().select('.select2-search-field-input', true).first();
12938     },
12939     
12940     
12941     getAutoCreateTouchView : function()
12942     {
12943         var id = Roo.id();
12944         
12945         var cfg = {
12946             cls: 'form-group' //input-group
12947         };
12948         
12949         var input =  {
12950             tag: 'input',
12951             id : id,
12952             type : this.inputType,
12953             cls : 'form-control x-combo-noedit',
12954             autocomplete: 'new-password',
12955             placeholder : this.placeholder || '',
12956             readonly : true
12957         };
12958         
12959         if (this.name) {
12960             input.name = this.name;
12961         }
12962         
12963         if (this.size) {
12964             input.cls += ' input-' + this.size;
12965         }
12966         
12967         if (this.disabled) {
12968             input.disabled = true;
12969         }
12970         
12971         var inputblock = {
12972             cls : '',
12973             cn : [
12974                 input
12975             ]
12976         };
12977         
12978         if(this.before){
12979             inputblock.cls += ' input-group';
12980             
12981             inputblock.cn.unshift({
12982                 tag :'span',
12983                 cls : 'input-group-addon',
12984                 html : this.before
12985             });
12986         }
12987         
12988         if(this.removable && !this.multiple){
12989             inputblock.cls += ' roo-removable';
12990             
12991             inputblock.cn.push({
12992                 tag: 'button',
12993                 html : 'x',
12994                 cls : 'roo-combo-removable-btn close'
12995             });
12996         }
12997
12998         if(this.hasFeedback && !this.allowBlank){
12999             
13000             inputblock.cls += ' has-feedback';
13001             
13002             inputblock.cn.push({
13003                 tag: 'span',
13004                 cls: 'glyphicon form-control-feedback'
13005             });
13006             
13007         }
13008         
13009         if (this.after) {
13010             
13011             inputblock.cls += (this.before) ? '' : ' input-group';
13012             
13013             inputblock.cn.push({
13014                 tag :'span',
13015                 cls : 'input-group-addon',
13016                 html : this.after
13017             });
13018         }
13019
13020         var box = {
13021             tag: 'div',
13022             cn: [
13023                 {
13024                     tag: 'input',
13025                     type : 'hidden',
13026                     cls: 'form-hidden-field'
13027                 },
13028                 inputblock
13029             ]
13030             
13031         };
13032         
13033         if(this.multiple){
13034             box = {
13035                 tag: 'div',
13036                 cn: [
13037                     {
13038                         tag: 'input',
13039                         type : 'hidden',
13040                         cls: 'form-hidden-field'
13041                     },
13042                     {
13043                         tag: 'ul',
13044                         cls: 'select2-choices',
13045                         cn:[
13046                             {
13047                                 tag: 'li',
13048                                 cls: 'select2-search-field',
13049                                 cn: [
13050
13051                                     inputblock
13052                                 ]
13053                             }
13054                         ]
13055                     }
13056                 ]
13057             }
13058         };
13059         
13060         var combobox = {
13061             cls: 'select2-container input-group',
13062             cn: [
13063                 box
13064             ]
13065         };
13066         
13067         if(this.multiple){
13068             combobox.cls += ' select2-container-multi';
13069         }
13070         
13071         var align = this.labelAlign || this.parentLabelAlign();
13072         
13073         cfg.cn = combobox;
13074         
13075         if(this.fieldLabel.length){
13076             
13077             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13078             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13079             
13080             cfg.cn = [
13081                 {
13082                     tag: 'label',
13083                     cls : 'control-label ' + lw,
13084                     html : this.fieldLabel
13085
13086                 },
13087                 {
13088                     cls : cw, 
13089                     cn: [
13090                         combobox
13091                     ]
13092                 }
13093             ];
13094         }
13095         
13096         var settings = this;
13097         
13098         ['xs','sm','md','lg'].map(function(size){
13099             if (settings[size]) {
13100                 cfg.cls += ' col-' + size + '-' + settings[size];
13101             }
13102         });
13103         
13104         return cfg;
13105     },
13106     
13107     initTouchView : function()
13108     {
13109         this.renderTouchView();
13110         
13111         this.inputEl().on("click", this.showTouchView, this);
13112         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13113         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13114         
13115         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13116         
13117         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13118         this.store.on('load', this.onTouchViewLoad, this);
13119         this.store.on('loadexception', this.onTouchViewLoadException, this);
13120         
13121         if(this.hiddenName){
13122             
13123             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13124             
13125             this.hiddenField.dom.value =
13126                 this.hiddenValue !== undefined ? this.hiddenValue :
13127                 this.value !== undefined ? this.value : '';
13128         
13129             this.el.dom.removeAttribute('name');
13130             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13131         }
13132         
13133         if(this.multiple){
13134             this.choices = this.el.select('ul.select2-choices', true).first();
13135             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13136         }
13137         
13138         if(this.removable && !this.multiple){
13139             var close = this.closeTriggerEl();
13140             
13141             if(close){
13142                 close.setVisibilityMode(Roo.Element.DISPALY).hide();
13143                 close.on('click', this.removeBtnClick, this, close);
13144             }
13145         }
13146         
13147         return;
13148         
13149         
13150     },
13151     
13152     renderTouchView : function()
13153     {
13154         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13155         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13156         
13157         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13158         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13159         
13160         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13161         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13162         this.touchViewBodyEl.setStyle('overflow', 'auto');
13163         
13164         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13165         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13166         
13167         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13168         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13169         
13170     },
13171     
13172     showTouchView : function()
13173     {
13174         this.touchViewHeaderEl.hide();
13175
13176         if(this.fieldLabel.length){
13177             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13178             this.touchViewHeaderEl.show();
13179         }
13180
13181         this.touchViewEl.show();
13182
13183         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13184         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13185
13186         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13187
13188         if(this.fieldLabel.length){
13189             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13190         }
13191
13192         this.touchViewBodyEl.setHeight(bodyHeight);
13193
13194         if(this.animate){
13195             var _this = this;
13196             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13197         }else{
13198             this.touchViewEl.addClass('in');
13199         }
13200
13201         this.doTouchViewQuery();
13202         
13203     },
13204     
13205     hideTouchView : function()
13206     {
13207         this.touchViewEl.removeClass('in');
13208
13209         if(this.animate){
13210             var _this = this;
13211             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13212         }else{
13213             this.touchViewEl.setStyle('display', 'none');
13214         }
13215         
13216     },
13217     
13218     setTouchViewValue : function()
13219     {
13220         if(this.multiple){
13221             this.clearItem();
13222         
13223             var _this = this;
13224
13225             Roo.each(this.tickItems, function(o){
13226                 this.addItem(o);
13227             }, this);
13228         }
13229         
13230         this.hideTouchView();
13231     },
13232     
13233     doTouchViewQuery : function()
13234     {
13235         Roo.log('doTouchViewQuery');
13236         
13237         var qe = {
13238             query: '',
13239             forceAll: true,
13240             combo: this,
13241             cancel:false
13242         };
13243         
13244         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13245             return false;
13246         }
13247         
13248         if(!this.alwaysQuery || this.mode == 'local'){
13249             this.onTouchViewLoad();
13250             return;
13251         }
13252         
13253         this.store.load();
13254     },
13255     
13256     onTouchViewBeforeLoad : function(combo,opts)
13257     {
13258         Roo.log('onTouchViewBeforeLoad');
13259         
13260         return;
13261     },
13262
13263     // private
13264     onTouchViewLoad : function()
13265     {
13266         Roo.log('onTouchViewLoad');
13267         
13268         if(this.store.getCount() < 1){
13269             this.onTouchViewEmptyResults();
13270             return;
13271         }
13272         
13273         this.clearTouchView();
13274         
13275         var rawValue = this.getRawValue();
13276         
13277         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13278         
13279         this.tickItems = [];
13280         
13281         this.store.data.each(function(d, rowIndex){
13282             var row = this.touchViewListGroup.createChild(template);
13283             
13284             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13285                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13286             }
13287             
13288             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13289                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13290             }
13291             
13292             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13293                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13294                 this.tickItems.push(d.data);
13295             }
13296             
13297             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13298             
13299         }, this);
13300         
13301         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13302         
13303         if(firstChecked){
13304             firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom);
13305         }
13306         
13307     },
13308     
13309     onTouchViewLoadException : function()
13310     {
13311         Roo.log('onTouchViewLoadException');
13312         
13313         this.hideTouchView();
13314     },
13315     
13316     onTouchViewEmptyResults : function()
13317     {
13318         Roo.log('onTouchViewEmptyResults');
13319         
13320         this.clearTouchView();
13321         
13322         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13323         
13324         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13325         
13326     },
13327     
13328     clearTouchView : function()
13329     {
13330         this.touchViewListGroup.dom.innerHTML = '';
13331     },
13332     
13333     onTouchViewClick : function(e, el, o)
13334     {
13335         e.preventDefault();
13336         
13337         var row = o.row;
13338         var rowIndex = o.rowIndex;
13339         
13340         var r = this.store.getAt(rowIndex);
13341         
13342         if(!this.multiple){
13343             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13344                 c.dom.removeAttribute('checked');
13345             }, this);
13346             
13347             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13348         
13349             this.setFromData(r.data);
13350             
13351             var close = this.closeTriggerEl();
13352         
13353             if(close){
13354                 close.show();
13355             }
13356
13357             this.hideTouchView();
13358             
13359             return;
13360         }
13361         
13362         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13363             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13364             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13365             return;
13366         }
13367         
13368         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13369         this.addItem(r.data);
13370         this.tickItems.push(r.data);
13371         
13372         
13373         
13374     }
13375     
13376
13377     /** 
13378     * @cfg {Boolean} grow 
13379     * @hide 
13380     */
13381     /** 
13382     * @cfg {Number} growMin 
13383     * @hide 
13384     */
13385     /** 
13386     * @cfg {Number} growMax 
13387     * @hide 
13388     */
13389     /**
13390      * @hide
13391      * @method autoSize
13392      */
13393 });
13394
13395 Roo.apply(Roo.bootstrap.ComboBox,  {
13396     
13397     header : {
13398         tag: 'div',
13399         cls: 'modal-header',
13400         cn: [
13401             {
13402                 tag: 'h4',
13403                 cls: 'modal-title'
13404             }
13405         ]
13406     },
13407     
13408     body : {
13409         tag: 'div',
13410         cls: 'modal-body',
13411         cn: [
13412             {
13413                 tag: 'ul',
13414                 cls: 'list-group'
13415             }
13416         ]
13417     },
13418     
13419     listItemRadio : {
13420         tag: 'li',
13421         cls: 'list-group-item',
13422         cn: [
13423             {
13424                 tag: 'span',
13425                 cls: 'roo-combobox-list-group-item-value'
13426             },
13427             {
13428                 tag: 'div',
13429                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13430                 cn: [
13431                     {
13432                         tag: 'input',
13433                         type: 'radio'
13434                     },
13435                     {
13436                         tag: 'label'
13437                     }
13438                 ]
13439             }
13440         ]
13441     },
13442     
13443     listItemCheckbox : {
13444         tag: 'li',
13445         cls: 'list-group-item',
13446         cn: [
13447             {
13448                 tag: 'span',
13449                 cls: 'roo-combobox-list-group-item-value'
13450             },
13451             {
13452                 tag: 'div',
13453                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13454                 cn: [
13455                     {
13456                         tag: 'input',
13457                         type: 'checkbox'
13458                     },
13459                     {
13460                         tag: 'label'
13461                     }
13462                 ]
13463             }
13464         ]
13465     },
13466     
13467     emptyResult : {
13468         tag: 'div',
13469         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13470     },
13471     
13472     footer : {
13473         tag: 'div',
13474         cls: 'modal-footer',
13475         cn: [
13476             {
13477                 tag: 'div',
13478                 cls: 'row',
13479                 cn: [
13480                     {
13481                         tag: 'div',
13482                         cls: 'col-xs-6 text-left',
13483                         cn: {
13484                             tag: 'button',
13485                             cls: 'btn btn-danger roo-touch-view-cancel',
13486                             html: 'Cancel'
13487                         }
13488                     },
13489                     {
13490                         tag: 'div',
13491                         cls: 'col-xs-6 text-right',
13492                         cn: {
13493                             tag: 'button',
13494                             cls: 'btn btn-success roo-touch-view-ok',
13495                             html: 'OK'
13496                         }
13497                     }
13498                 ]
13499             }
13500         ]
13501         
13502     }
13503 });
13504
13505 Roo.apply(Roo.bootstrap.ComboBox,  {
13506     
13507     touchViewTemplate : {
13508         tag: 'div',
13509         cls: 'modal fade roo-combobox-touch-view',
13510         cn: [
13511             {
13512                 tag: 'div',
13513                 cls: 'modal-dialog',
13514                 cn: [
13515                     {
13516                         tag: 'div',
13517                         cls: 'modal-content',
13518                         cn: [
13519                             Roo.bootstrap.ComboBox.header,
13520                             Roo.bootstrap.ComboBox.body,
13521                             Roo.bootstrap.ComboBox.footer
13522                         ]
13523                     }
13524                 ]
13525             }
13526         ]
13527     }
13528 });/*
13529  * Based on:
13530  * Ext JS Library 1.1.1
13531  * Copyright(c) 2006-2007, Ext JS, LLC.
13532  *
13533  * Originally Released Under LGPL - original licence link has changed is not relivant.
13534  *
13535  * Fork - LGPL
13536  * <script type="text/javascript">
13537  */
13538
13539 /**
13540  * @class Roo.View
13541  * @extends Roo.util.Observable
13542  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13543  * This class also supports single and multi selection modes. <br>
13544  * Create a data model bound view:
13545  <pre><code>
13546  var store = new Roo.data.Store(...);
13547
13548  var view = new Roo.View({
13549     el : "my-element",
13550     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13551  
13552     singleSelect: true,
13553     selectedClass: "ydataview-selected",
13554     store: store
13555  });
13556
13557  // listen for node click?
13558  view.on("click", function(vw, index, node, e){
13559  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13560  });
13561
13562  // load XML data
13563  dataModel.load("foobar.xml");
13564  </code></pre>
13565  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13566  * <br><br>
13567  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13568  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13569  * 
13570  * Note: old style constructor is still suported (container, template, config)
13571  * 
13572  * @constructor
13573  * Create a new View
13574  * @param {Object} config The config object
13575  * 
13576  */
13577 Roo.View = function(config, depreciated_tpl, depreciated_config){
13578     
13579     this.parent = false;
13580     
13581     if (typeof(depreciated_tpl) == 'undefined') {
13582         // new way.. - universal constructor.
13583         Roo.apply(this, config);
13584         this.el  = Roo.get(this.el);
13585     } else {
13586         // old format..
13587         this.el  = Roo.get(config);
13588         this.tpl = depreciated_tpl;
13589         Roo.apply(this, depreciated_config);
13590     }
13591     this.wrapEl  = this.el.wrap().wrap();
13592     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13593     
13594     
13595     if(typeof(this.tpl) == "string"){
13596         this.tpl = new Roo.Template(this.tpl);
13597     } else {
13598         // support xtype ctors..
13599         this.tpl = new Roo.factory(this.tpl, Roo);
13600     }
13601     
13602     
13603     this.tpl.compile();
13604     
13605     /** @private */
13606     this.addEvents({
13607         /**
13608          * @event beforeclick
13609          * Fires before a click is processed. Returns false to cancel the default action.
13610          * @param {Roo.View} this
13611          * @param {Number} index The index of the target node
13612          * @param {HTMLElement} node The target node
13613          * @param {Roo.EventObject} e The raw event object
13614          */
13615             "beforeclick" : true,
13616         /**
13617          * @event click
13618          * Fires when a template node is clicked.
13619          * @param {Roo.View} this
13620          * @param {Number} index The index of the target node
13621          * @param {HTMLElement} node The target node
13622          * @param {Roo.EventObject} e The raw event object
13623          */
13624             "click" : true,
13625         /**
13626          * @event dblclick
13627          * Fires when a template node is double clicked.
13628          * @param {Roo.View} this
13629          * @param {Number} index The index of the target node
13630          * @param {HTMLElement} node The target node
13631          * @param {Roo.EventObject} e The raw event object
13632          */
13633             "dblclick" : true,
13634         /**
13635          * @event contextmenu
13636          * Fires when a template node is right clicked.
13637          * @param {Roo.View} this
13638          * @param {Number} index The index of the target node
13639          * @param {HTMLElement} node The target node
13640          * @param {Roo.EventObject} e The raw event object
13641          */
13642             "contextmenu" : true,
13643         /**
13644          * @event selectionchange
13645          * Fires when the selected nodes change.
13646          * @param {Roo.View} this
13647          * @param {Array} selections Array of the selected nodes
13648          */
13649             "selectionchange" : true,
13650     
13651         /**
13652          * @event beforeselect
13653          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13654          * @param {Roo.View} this
13655          * @param {HTMLElement} node The node to be selected
13656          * @param {Array} selections Array of currently selected nodes
13657          */
13658             "beforeselect" : true,
13659         /**
13660          * @event preparedata
13661          * Fires on every row to render, to allow you to change the data.
13662          * @param {Roo.View} this
13663          * @param {Object} data to be rendered (change this)
13664          */
13665           "preparedata" : true
13666           
13667           
13668         });
13669
13670
13671
13672     this.el.on({
13673         "click": this.onClick,
13674         "dblclick": this.onDblClick,
13675         "contextmenu": this.onContextMenu,
13676         scope:this
13677     });
13678
13679     this.selections = [];
13680     this.nodes = [];
13681     this.cmp = new Roo.CompositeElementLite([]);
13682     if(this.store){
13683         this.store = Roo.factory(this.store, Roo.data);
13684         this.setStore(this.store, true);
13685     }
13686     
13687     if ( this.footer && this.footer.xtype) {
13688            
13689          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13690         
13691         this.footer.dataSource = this.store
13692         this.footer.container = fctr;
13693         this.footer = Roo.factory(this.footer, Roo);
13694         fctr.insertFirst(this.el);
13695         
13696         // this is a bit insane - as the paging toolbar seems to detach the el..
13697 //        dom.parentNode.parentNode.parentNode
13698          // they get detached?
13699     }
13700     
13701     
13702     Roo.View.superclass.constructor.call(this);
13703     
13704     
13705 };
13706
13707 Roo.extend(Roo.View, Roo.util.Observable, {
13708     
13709      /**
13710      * @cfg {Roo.data.Store} store Data store to load data from.
13711      */
13712     store : false,
13713     
13714     /**
13715      * @cfg {String|Roo.Element} el The container element.
13716      */
13717     el : '',
13718     
13719     /**
13720      * @cfg {String|Roo.Template} tpl The template used by this View 
13721      */
13722     tpl : false,
13723     /**
13724      * @cfg {String} dataName the named area of the template to use as the data area
13725      *                          Works with domtemplates roo-name="name"
13726      */
13727     dataName: false,
13728     /**
13729      * @cfg {String} selectedClass The css class to add to selected nodes
13730      */
13731     selectedClass : "x-view-selected",
13732      /**
13733      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13734      */
13735     emptyText : "",
13736     
13737     /**
13738      * @cfg {String} text to display on mask (default Loading)
13739      */
13740     mask : false,
13741     /**
13742      * @cfg {Boolean} multiSelect Allow multiple selection
13743      */
13744     multiSelect : false,
13745     /**
13746      * @cfg {Boolean} singleSelect Allow single selection
13747      */
13748     singleSelect:  false,
13749     
13750     /**
13751      * @cfg {Boolean} toggleSelect - selecting 
13752      */
13753     toggleSelect : false,
13754     
13755     /**
13756      * @cfg {Boolean} tickable - selecting 
13757      */
13758     tickable : false,
13759     
13760     /**
13761      * Returns the element this view is bound to.
13762      * @return {Roo.Element}
13763      */
13764     getEl : function(){
13765         return this.wrapEl;
13766     },
13767     
13768     
13769
13770     /**
13771      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13772      */
13773     refresh : function(){
13774         //Roo.log('refresh');
13775         var t = this.tpl;
13776         
13777         // if we are using something like 'domtemplate', then
13778         // the what gets used is:
13779         // t.applySubtemplate(NAME, data, wrapping data..)
13780         // the outer template then get' applied with
13781         //     the store 'extra data'
13782         // and the body get's added to the
13783         //      roo-name="data" node?
13784         //      <span class='roo-tpl-{name}'></span> ?????
13785         
13786         
13787         
13788         this.clearSelections();
13789         this.el.update("");
13790         var html = [];
13791         var records = this.store.getRange();
13792         if(records.length < 1) {
13793             
13794             // is this valid??  = should it render a template??
13795             
13796             this.el.update(this.emptyText);
13797             return;
13798         }
13799         var el = this.el;
13800         if (this.dataName) {
13801             this.el.update(t.apply(this.store.meta)); //????
13802             el = this.el.child('.roo-tpl-' + this.dataName);
13803         }
13804         
13805         for(var i = 0, len = records.length; i < len; i++){
13806             var data = this.prepareData(records[i].data, i, records[i]);
13807             this.fireEvent("preparedata", this, data, i, records[i]);
13808             
13809             var d = Roo.apply({}, data);
13810             
13811             if(this.tickable){
13812                 Roo.apply(d, {'roo-id' : Roo.id()});
13813                 
13814                 var _this = this;
13815             
13816                 Roo.each(this.parent.item, function(item){
13817                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13818                         return;
13819                     }
13820                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13821                 });
13822             }
13823             
13824             html[html.length] = Roo.util.Format.trim(
13825                 this.dataName ?
13826                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13827                     t.apply(d)
13828             );
13829         }
13830         
13831         
13832         
13833         el.update(html.join(""));
13834         this.nodes = el.dom.childNodes;
13835         this.updateIndexes(0);
13836     },
13837     
13838
13839     /**
13840      * Function to override to reformat the data that is sent to
13841      * the template for each node.
13842      * DEPRICATED - use the preparedata event handler.
13843      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13844      * a JSON object for an UpdateManager bound view).
13845      */
13846     prepareData : function(data, index, record)
13847     {
13848         this.fireEvent("preparedata", this, data, index, record);
13849         return data;
13850     },
13851
13852     onUpdate : function(ds, record){
13853         // Roo.log('on update');   
13854         this.clearSelections();
13855         var index = this.store.indexOf(record);
13856         var n = this.nodes[index];
13857         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13858         n.parentNode.removeChild(n);
13859         this.updateIndexes(index, index);
13860     },
13861
13862     
13863     
13864 // --------- FIXME     
13865     onAdd : function(ds, records, index)
13866     {
13867         //Roo.log(['on Add', ds, records, index] );        
13868         this.clearSelections();
13869         if(this.nodes.length == 0){
13870             this.refresh();
13871             return;
13872         }
13873         var n = this.nodes[index];
13874         for(var i = 0, len = records.length; i < len; i++){
13875             var d = this.prepareData(records[i].data, i, records[i]);
13876             if(n){
13877                 this.tpl.insertBefore(n, d);
13878             }else{
13879                 
13880                 this.tpl.append(this.el, d);
13881             }
13882         }
13883         this.updateIndexes(index);
13884     },
13885
13886     onRemove : function(ds, record, index){
13887        // Roo.log('onRemove');
13888         this.clearSelections();
13889         var el = this.dataName  ?
13890             this.el.child('.roo-tpl-' + this.dataName) :
13891             this.el; 
13892         
13893         el.dom.removeChild(this.nodes[index]);
13894         this.updateIndexes(index);
13895     },
13896
13897     /**
13898      * Refresh an individual node.
13899      * @param {Number} index
13900      */
13901     refreshNode : function(index){
13902         this.onUpdate(this.store, this.store.getAt(index));
13903     },
13904
13905     updateIndexes : function(startIndex, endIndex){
13906         var ns = this.nodes;
13907         startIndex = startIndex || 0;
13908         endIndex = endIndex || ns.length - 1;
13909         for(var i = startIndex; i <= endIndex; i++){
13910             ns[i].nodeIndex = i;
13911         }
13912     },
13913
13914     /**
13915      * Changes the data store this view uses and refresh the view.
13916      * @param {Store} store
13917      */
13918     setStore : function(store, initial){
13919         if(!initial && this.store){
13920             this.store.un("datachanged", this.refresh);
13921             this.store.un("add", this.onAdd);
13922             this.store.un("remove", this.onRemove);
13923             this.store.un("update", this.onUpdate);
13924             this.store.un("clear", this.refresh);
13925             this.store.un("beforeload", this.onBeforeLoad);
13926             this.store.un("load", this.onLoad);
13927             this.store.un("loadexception", this.onLoad);
13928         }
13929         if(store){
13930           
13931             store.on("datachanged", this.refresh, this);
13932             store.on("add", this.onAdd, this);
13933             store.on("remove", this.onRemove, this);
13934             store.on("update", this.onUpdate, this);
13935             store.on("clear", this.refresh, this);
13936             store.on("beforeload", this.onBeforeLoad, this);
13937             store.on("load", this.onLoad, this);
13938             store.on("loadexception", this.onLoad, this);
13939         }
13940         
13941         if(store){
13942             this.refresh();
13943         }
13944     },
13945     /**
13946      * onbeforeLoad - masks the loading area.
13947      *
13948      */
13949     onBeforeLoad : function(store,opts)
13950     {
13951          //Roo.log('onBeforeLoad');   
13952         if (!opts.add) {
13953             this.el.update("");
13954         }
13955         this.el.mask(this.mask ? this.mask : "Loading" ); 
13956     },
13957     onLoad : function ()
13958     {
13959         this.el.unmask();
13960     },
13961     
13962
13963     /**
13964      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13965      * @param {HTMLElement} node
13966      * @return {HTMLElement} The template node
13967      */
13968     findItemFromChild : function(node){
13969         var el = this.dataName  ?
13970             this.el.child('.roo-tpl-' + this.dataName,true) :
13971             this.el.dom; 
13972         
13973         if(!node || node.parentNode == el){
13974                     return node;
13975             }
13976             var p = node.parentNode;
13977             while(p && p != el){
13978             if(p.parentNode == el){
13979                 return p;
13980             }
13981             p = p.parentNode;
13982         }
13983             return null;
13984     },
13985
13986     /** @ignore */
13987     onClick : function(e){
13988         var item = this.findItemFromChild(e.getTarget());
13989         if(item){
13990             var index = this.indexOf(item);
13991             if(this.onItemClick(item, index, e) !== false){
13992                 this.fireEvent("click", this, index, item, e);
13993             }
13994         }else{
13995             this.clearSelections();
13996         }
13997     },
13998
13999     /** @ignore */
14000     onContextMenu : function(e){
14001         var item = this.findItemFromChild(e.getTarget());
14002         if(item){
14003             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14004         }
14005     },
14006
14007     /** @ignore */
14008     onDblClick : function(e){
14009         var item = this.findItemFromChild(e.getTarget());
14010         if(item){
14011             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14012         }
14013     },
14014
14015     onItemClick : function(item, index, e)
14016     {
14017         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14018             return false;
14019         }
14020         if (this.toggleSelect) {
14021             var m = this.isSelected(item) ? 'unselect' : 'select';
14022             //Roo.log(m);
14023             var _t = this;
14024             _t[m](item, true, false);
14025             return true;
14026         }
14027         if(this.multiSelect || this.singleSelect){
14028             if(this.multiSelect && e.shiftKey && this.lastSelection){
14029                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14030             }else{
14031                 this.select(item, this.multiSelect && e.ctrlKey);
14032                 this.lastSelection = item;
14033             }
14034             
14035             if(!this.tickable){
14036                 e.preventDefault();
14037             }
14038             
14039         }
14040         return true;
14041     },
14042
14043     /**
14044      * Get the number of selected nodes.
14045      * @return {Number}
14046      */
14047     getSelectionCount : function(){
14048         return this.selections.length;
14049     },
14050
14051     /**
14052      * Get the currently selected nodes.
14053      * @return {Array} An array of HTMLElements
14054      */
14055     getSelectedNodes : function(){
14056         return this.selections;
14057     },
14058
14059     /**
14060      * Get the indexes of the selected nodes.
14061      * @return {Array}
14062      */
14063     getSelectedIndexes : function(){
14064         var indexes = [], s = this.selections;
14065         for(var i = 0, len = s.length; i < len; i++){
14066             indexes.push(s[i].nodeIndex);
14067         }
14068         return indexes;
14069     },
14070
14071     /**
14072      * Clear all selections
14073      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14074      */
14075     clearSelections : function(suppressEvent){
14076         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14077             this.cmp.elements = this.selections;
14078             this.cmp.removeClass(this.selectedClass);
14079             this.selections = [];
14080             if(!suppressEvent){
14081                 this.fireEvent("selectionchange", this, this.selections);
14082             }
14083         }
14084     },
14085
14086     /**
14087      * Returns true if the passed node is selected
14088      * @param {HTMLElement/Number} node The node or node index
14089      * @return {Boolean}
14090      */
14091     isSelected : function(node){
14092         var s = this.selections;
14093         if(s.length < 1){
14094             return false;
14095         }
14096         node = this.getNode(node);
14097         return s.indexOf(node) !== -1;
14098     },
14099
14100     /**
14101      * Selects nodes.
14102      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14103      * @param {Boolean} keepExisting (optional) true to keep existing selections
14104      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14105      */
14106     select : function(nodeInfo, keepExisting, suppressEvent){
14107         if(nodeInfo instanceof Array){
14108             if(!keepExisting){
14109                 this.clearSelections(true);
14110             }
14111             for(var i = 0, len = nodeInfo.length; i < len; i++){
14112                 this.select(nodeInfo[i], true, true);
14113             }
14114             return;
14115         } 
14116         var node = this.getNode(nodeInfo);
14117         if(!node || this.isSelected(node)){
14118             return; // already selected.
14119         }
14120         if(!keepExisting){
14121             this.clearSelections(true);
14122         }
14123         
14124         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14125             Roo.fly(node).addClass(this.selectedClass);
14126             this.selections.push(node);
14127             if(!suppressEvent){
14128                 this.fireEvent("selectionchange", this, this.selections);
14129             }
14130         }
14131         
14132         
14133     },
14134       /**
14135      * Unselects nodes.
14136      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14137      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14138      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14139      */
14140     unselect : function(nodeInfo, keepExisting, suppressEvent)
14141     {
14142         if(nodeInfo instanceof Array){
14143             Roo.each(this.selections, function(s) {
14144                 this.unselect(s, nodeInfo);
14145             }, this);
14146             return;
14147         }
14148         var node = this.getNode(nodeInfo);
14149         if(!node || !this.isSelected(node)){
14150             //Roo.log("not selected");
14151             return; // not selected.
14152         }
14153         // fireevent???
14154         var ns = [];
14155         Roo.each(this.selections, function(s) {
14156             if (s == node ) {
14157                 Roo.fly(node).removeClass(this.selectedClass);
14158
14159                 return;
14160             }
14161             ns.push(s);
14162         },this);
14163         
14164         this.selections= ns;
14165         this.fireEvent("selectionchange", this, this.selections);
14166     },
14167
14168     /**
14169      * Gets a template node.
14170      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14171      * @return {HTMLElement} The node or null if it wasn't found
14172      */
14173     getNode : function(nodeInfo){
14174         if(typeof nodeInfo == "string"){
14175             return document.getElementById(nodeInfo);
14176         }else if(typeof nodeInfo == "number"){
14177             return this.nodes[nodeInfo];
14178         }
14179         return nodeInfo;
14180     },
14181
14182     /**
14183      * Gets a range template nodes.
14184      * @param {Number} startIndex
14185      * @param {Number} endIndex
14186      * @return {Array} An array of nodes
14187      */
14188     getNodes : function(start, end){
14189         var ns = this.nodes;
14190         start = start || 0;
14191         end = typeof end == "undefined" ? ns.length - 1 : end;
14192         var nodes = [];
14193         if(start <= end){
14194             for(var i = start; i <= end; i++){
14195                 nodes.push(ns[i]);
14196             }
14197         } else{
14198             for(var i = start; i >= end; i--){
14199                 nodes.push(ns[i]);
14200             }
14201         }
14202         return nodes;
14203     },
14204
14205     /**
14206      * Finds the index of the passed node
14207      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14208      * @return {Number} The index of the node or -1
14209      */
14210     indexOf : function(node){
14211         node = this.getNode(node);
14212         if(typeof node.nodeIndex == "number"){
14213             return node.nodeIndex;
14214         }
14215         var ns = this.nodes;
14216         for(var i = 0, len = ns.length; i < len; i++){
14217             if(ns[i] == node){
14218                 return i;
14219             }
14220         }
14221         return -1;
14222     }
14223 });
14224 /*
14225  * - LGPL
14226  *
14227  * based on jquery fullcalendar
14228  * 
14229  */
14230
14231 Roo.bootstrap = Roo.bootstrap || {};
14232 /**
14233  * @class Roo.bootstrap.Calendar
14234  * @extends Roo.bootstrap.Component
14235  * Bootstrap Calendar class
14236  * @cfg {Boolean} loadMask (true|false) default false
14237  * @cfg {Object} header generate the user specific header of the calendar, default false
14238
14239  * @constructor
14240  * Create a new Container
14241  * @param {Object} config The config object
14242  */
14243
14244
14245
14246 Roo.bootstrap.Calendar = function(config){
14247     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14248      this.addEvents({
14249         /**
14250              * @event select
14251              * Fires when a date is selected
14252              * @param {DatePicker} this
14253              * @param {Date} date The selected date
14254              */
14255         'select': true,
14256         /**
14257              * @event monthchange
14258              * Fires when the displayed month changes 
14259              * @param {DatePicker} this
14260              * @param {Date} date The selected month
14261              */
14262         'monthchange': true,
14263         /**
14264              * @event evententer
14265              * Fires when mouse over an event
14266              * @param {Calendar} this
14267              * @param {event} Event
14268              */
14269         'evententer': true,
14270         /**
14271              * @event eventleave
14272              * Fires when the mouse leaves an
14273              * @param {Calendar} this
14274              * @param {event}
14275              */
14276         'eventleave': true,
14277         /**
14278              * @event eventclick
14279              * Fires when the mouse click an
14280              * @param {Calendar} this
14281              * @param {event}
14282              */
14283         'eventclick': true
14284         
14285     });
14286
14287 };
14288
14289 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14290     
14291      /**
14292      * @cfg {Number} startDay
14293      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14294      */
14295     startDay : 0,
14296     
14297     loadMask : false,
14298     
14299     header : false,
14300       
14301     getAutoCreate : function(){
14302         
14303         
14304         var fc_button = function(name, corner, style, content ) {
14305             return Roo.apply({},{
14306                 tag : 'span',
14307                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14308                          (corner.length ?
14309                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14310                             ''
14311                         ),
14312                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14313                 unselectable: 'on'
14314             });
14315         };
14316         
14317         var header = {};
14318         
14319         if(!this.header){
14320             header = {
14321                 tag : 'table',
14322                 cls : 'fc-header',
14323                 style : 'width:100%',
14324                 cn : [
14325                     {
14326                         tag: 'tr',
14327                         cn : [
14328                             {
14329                                 tag : 'td',
14330                                 cls : 'fc-header-left',
14331                                 cn : [
14332                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14333                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14334                                     { tag: 'span', cls: 'fc-header-space' },
14335                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14336
14337
14338                                 ]
14339                             },
14340
14341                             {
14342                                 tag : 'td',
14343                                 cls : 'fc-header-center',
14344                                 cn : [
14345                                     {
14346                                         tag: 'span',
14347                                         cls: 'fc-header-title',
14348                                         cn : {
14349                                             tag: 'H2',
14350                                             html : 'month / year'
14351                                         }
14352                                     }
14353
14354                                 ]
14355                             },
14356                             {
14357                                 tag : 'td',
14358                                 cls : 'fc-header-right',
14359                                 cn : [
14360                               /*      fc_button('month', 'left', '', 'month' ),
14361                                     fc_button('week', '', '', 'week' ),
14362                                     fc_button('day', 'right', '', 'day' )
14363                                 */    
14364
14365                                 ]
14366                             }
14367
14368                         ]
14369                     }
14370                 ]
14371             };
14372         }
14373         
14374         header = this.header;
14375         
14376        
14377         var cal_heads = function() {
14378             var ret = [];
14379             // fixme - handle this.
14380             
14381             for (var i =0; i < Date.dayNames.length; i++) {
14382                 var d = Date.dayNames[i];
14383                 ret.push({
14384                     tag: 'th',
14385                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14386                     html : d.substring(0,3)
14387                 });
14388                 
14389             }
14390             ret[0].cls += ' fc-first';
14391             ret[6].cls += ' fc-last';
14392             return ret;
14393         };
14394         var cal_cell = function(n) {
14395             return  {
14396                 tag: 'td',
14397                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14398                 cn : [
14399                     {
14400                         cn : [
14401                             {
14402                                 cls: 'fc-day-number',
14403                                 html: 'D'
14404                             },
14405                             {
14406                                 cls: 'fc-day-content',
14407                              
14408                                 cn : [
14409                                      {
14410                                         style: 'position: relative;' // height: 17px;
14411                                     }
14412                                 ]
14413                             }
14414                             
14415                             
14416                         ]
14417                     }
14418                 ]
14419                 
14420             }
14421         };
14422         var cal_rows = function() {
14423             
14424             var ret = [];
14425             for (var r = 0; r < 6; r++) {
14426                 var row= {
14427                     tag : 'tr',
14428                     cls : 'fc-week',
14429                     cn : []
14430                 };
14431                 
14432                 for (var i =0; i < Date.dayNames.length; i++) {
14433                     var d = Date.dayNames[i];
14434                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14435
14436                 }
14437                 row.cn[0].cls+=' fc-first';
14438                 row.cn[0].cn[0].style = 'min-height:90px';
14439                 row.cn[6].cls+=' fc-last';
14440                 ret.push(row);
14441                 
14442             }
14443             ret[0].cls += ' fc-first';
14444             ret[4].cls += ' fc-prev-last';
14445             ret[5].cls += ' fc-last';
14446             return ret;
14447             
14448         };
14449         
14450         var cal_table = {
14451             tag: 'table',
14452             cls: 'fc-border-separate',
14453             style : 'width:100%',
14454             cellspacing  : 0,
14455             cn : [
14456                 { 
14457                     tag: 'thead',
14458                     cn : [
14459                         { 
14460                             tag: 'tr',
14461                             cls : 'fc-first fc-last',
14462                             cn : cal_heads()
14463                         }
14464                     ]
14465                 },
14466                 { 
14467                     tag: 'tbody',
14468                     cn : cal_rows()
14469                 }
14470                   
14471             ]
14472         };
14473          
14474          var cfg = {
14475             cls : 'fc fc-ltr',
14476             cn : [
14477                 header,
14478                 {
14479                     cls : 'fc-content',
14480                     style : "position: relative;",
14481                     cn : [
14482                         {
14483                             cls : 'fc-view fc-view-month fc-grid',
14484                             style : 'position: relative',
14485                             unselectable : 'on',
14486                             cn : [
14487                                 {
14488                                     cls : 'fc-event-container',
14489                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14490                                 },
14491                                 cal_table
14492                             ]
14493                         }
14494                     ]
14495     
14496                 }
14497            ] 
14498             
14499         };
14500         
14501          
14502         
14503         return cfg;
14504     },
14505     
14506     
14507     initEvents : function()
14508     {
14509         if(!this.store){
14510             throw "can not find store for calendar";
14511         }
14512         
14513         var mark = {
14514             tag: "div",
14515             cls:"x-dlg-mask",
14516             style: "text-align:center",
14517             cn: [
14518                 {
14519                     tag: "div",
14520                     style: "background-color:white;width:50%;margin:250 auto",
14521                     cn: [
14522                         {
14523                             tag: "img",
14524                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14525                         },
14526                         {
14527                             tag: "span",
14528                             html: "Loading"
14529                         }
14530                         
14531                     ]
14532                 }
14533             ]
14534         }
14535         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14536         
14537         var size = this.el.select('.fc-content', true).first().getSize();
14538         this.maskEl.setSize(size.width, size.height);
14539         this.maskEl.enableDisplayMode("block");
14540         if(!this.loadMask){
14541             this.maskEl.hide();
14542         }
14543         
14544         this.store = Roo.factory(this.store, Roo.data);
14545         this.store.on('load', this.onLoad, this);
14546         this.store.on('beforeload', this.onBeforeLoad, this);
14547         
14548         this.resize();
14549         
14550         this.cells = this.el.select('.fc-day',true);
14551         //Roo.log(this.cells);
14552         this.textNodes = this.el.query('.fc-day-number');
14553         this.cells.addClassOnOver('fc-state-hover');
14554         
14555         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14556         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14557         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14558         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14559         
14560         this.on('monthchange', this.onMonthChange, this);
14561         
14562         this.update(new Date().clearTime());
14563     },
14564     
14565     resize : function() {
14566         var sz  = this.el.getSize();
14567         
14568         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14569         this.el.select('.fc-day-content div',true).setHeight(34);
14570     },
14571     
14572     
14573     // private
14574     showPrevMonth : function(e){
14575         this.update(this.activeDate.add("mo", -1));
14576     },
14577     showToday : function(e){
14578         this.update(new Date().clearTime());
14579     },
14580     // private
14581     showNextMonth : function(e){
14582         this.update(this.activeDate.add("mo", 1));
14583     },
14584
14585     // private
14586     showPrevYear : function(){
14587         this.update(this.activeDate.add("y", -1));
14588     },
14589
14590     // private
14591     showNextYear : function(){
14592         this.update(this.activeDate.add("y", 1));
14593     },
14594
14595     
14596    // private
14597     update : function(date)
14598     {
14599         var vd = this.activeDate;
14600         this.activeDate = date;
14601 //        if(vd && this.el){
14602 //            var t = date.getTime();
14603 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14604 //                Roo.log('using add remove');
14605 //                
14606 //                this.fireEvent('monthchange', this, date);
14607 //                
14608 //                this.cells.removeClass("fc-state-highlight");
14609 //                this.cells.each(function(c){
14610 //                   if(c.dateValue == t){
14611 //                       c.addClass("fc-state-highlight");
14612 //                       setTimeout(function(){
14613 //                            try{c.dom.firstChild.focus();}catch(e){}
14614 //                       }, 50);
14615 //                       return false;
14616 //                   }
14617 //                   return true;
14618 //                });
14619 //                return;
14620 //            }
14621 //        }
14622         
14623         var days = date.getDaysInMonth();
14624         
14625         var firstOfMonth = date.getFirstDateOfMonth();
14626         var startingPos = firstOfMonth.getDay()-this.startDay;
14627         
14628         if(startingPos < this.startDay){
14629             startingPos += 7;
14630         }
14631         
14632         var pm = date.add(Date.MONTH, -1);
14633         var prevStart = pm.getDaysInMonth()-startingPos;
14634 //        
14635         this.cells = this.el.select('.fc-day',true);
14636         this.textNodes = this.el.query('.fc-day-number');
14637         this.cells.addClassOnOver('fc-state-hover');
14638         
14639         var cells = this.cells.elements;
14640         var textEls = this.textNodes;
14641         
14642         Roo.each(cells, function(cell){
14643             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14644         });
14645         
14646         days += startingPos;
14647
14648         // convert everything to numbers so it's fast
14649         var day = 86400000;
14650         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14651         //Roo.log(d);
14652         //Roo.log(pm);
14653         //Roo.log(prevStart);
14654         
14655         var today = new Date().clearTime().getTime();
14656         var sel = date.clearTime().getTime();
14657         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14658         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14659         var ddMatch = this.disabledDatesRE;
14660         var ddText = this.disabledDatesText;
14661         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14662         var ddaysText = this.disabledDaysText;
14663         var format = this.format;
14664         
14665         var setCellClass = function(cal, cell){
14666             cell.row = 0;
14667             cell.events = [];
14668             cell.more = [];
14669             //Roo.log('set Cell Class');
14670             cell.title = "";
14671             var t = d.getTime();
14672             
14673             //Roo.log(d);
14674             
14675             cell.dateValue = t;
14676             if(t == today){
14677                 cell.className += " fc-today";
14678                 cell.className += " fc-state-highlight";
14679                 cell.title = cal.todayText;
14680             }
14681             if(t == sel){
14682                 // disable highlight in other month..
14683                 //cell.className += " fc-state-highlight";
14684                 
14685             }
14686             // disabling
14687             if(t < min) {
14688                 cell.className = " fc-state-disabled";
14689                 cell.title = cal.minText;
14690                 return;
14691             }
14692             if(t > max) {
14693                 cell.className = " fc-state-disabled";
14694                 cell.title = cal.maxText;
14695                 return;
14696             }
14697             if(ddays){
14698                 if(ddays.indexOf(d.getDay()) != -1){
14699                     cell.title = ddaysText;
14700                     cell.className = " fc-state-disabled";
14701                 }
14702             }
14703             if(ddMatch && format){
14704                 var fvalue = d.dateFormat(format);
14705                 if(ddMatch.test(fvalue)){
14706                     cell.title = ddText.replace("%0", fvalue);
14707                     cell.className = " fc-state-disabled";
14708                 }
14709             }
14710             
14711             if (!cell.initialClassName) {
14712                 cell.initialClassName = cell.dom.className;
14713             }
14714             
14715             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14716         };
14717
14718         var i = 0;
14719         
14720         for(; i < startingPos; i++) {
14721             textEls[i].innerHTML = (++prevStart);
14722             d.setDate(d.getDate()+1);
14723             
14724             cells[i].className = "fc-past fc-other-month";
14725             setCellClass(this, cells[i]);
14726         }
14727         
14728         var intDay = 0;
14729         
14730         for(; i < days; i++){
14731             intDay = i - startingPos + 1;
14732             textEls[i].innerHTML = (intDay);
14733             d.setDate(d.getDate()+1);
14734             
14735             cells[i].className = ''; // "x-date-active";
14736             setCellClass(this, cells[i]);
14737         }
14738         var extraDays = 0;
14739         
14740         for(; i < 42; i++) {
14741             textEls[i].innerHTML = (++extraDays);
14742             d.setDate(d.getDate()+1);
14743             
14744             cells[i].className = "fc-future fc-other-month";
14745             setCellClass(this, cells[i]);
14746         }
14747         
14748         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14749         
14750         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14751         
14752         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14753         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14754         
14755         if(totalRows != 6){
14756             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14757             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14758         }
14759         
14760         this.fireEvent('monthchange', this, date);
14761         
14762         
14763         /*
14764         if(!this.internalRender){
14765             var main = this.el.dom.firstChild;
14766             var w = main.offsetWidth;
14767             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14768             Roo.fly(main).setWidth(w);
14769             this.internalRender = true;
14770             // opera does not respect the auto grow header center column
14771             // then, after it gets a width opera refuses to recalculate
14772             // without a second pass
14773             if(Roo.isOpera && !this.secondPass){
14774                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14775                 this.secondPass = true;
14776                 this.update.defer(10, this, [date]);
14777             }
14778         }
14779         */
14780         
14781     },
14782     
14783     findCell : function(dt) {
14784         dt = dt.clearTime().getTime();
14785         var ret = false;
14786         this.cells.each(function(c){
14787             //Roo.log("check " +c.dateValue + '?=' + dt);
14788             if(c.dateValue == dt){
14789                 ret = c;
14790                 return false;
14791             }
14792             return true;
14793         });
14794         
14795         return ret;
14796     },
14797     
14798     findCells : function(ev) {
14799         var s = ev.start.clone().clearTime().getTime();
14800        // Roo.log(s);
14801         var e= ev.end.clone().clearTime().getTime();
14802        // Roo.log(e);
14803         var ret = [];
14804         this.cells.each(function(c){
14805              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14806             
14807             if(c.dateValue > e){
14808                 return ;
14809             }
14810             if(c.dateValue < s){
14811                 return ;
14812             }
14813             ret.push(c);
14814         });
14815         
14816         return ret;    
14817     },
14818     
14819 //    findBestRow: function(cells)
14820 //    {
14821 //        var ret = 0;
14822 //        
14823 //        for (var i =0 ; i < cells.length;i++) {
14824 //            ret  = Math.max(cells[i].rows || 0,ret);
14825 //        }
14826 //        return ret;
14827 //        
14828 //    },
14829     
14830     
14831     addItem : function(ev)
14832     {
14833         // look for vertical location slot in
14834         var cells = this.findCells(ev);
14835         
14836 //        ev.row = this.findBestRow(cells);
14837         
14838         // work out the location.
14839         
14840         var crow = false;
14841         var rows = [];
14842         for(var i =0; i < cells.length; i++) {
14843             
14844             cells[i].row = cells[0].row;
14845             
14846             if(i == 0){
14847                 cells[i].row = cells[i].row + 1;
14848             }
14849             
14850             if (!crow) {
14851                 crow = {
14852                     start : cells[i],
14853                     end :  cells[i]
14854                 };
14855                 continue;
14856             }
14857             if (crow.start.getY() == cells[i].getY()) {
14858                 // on same row.
14859                 crow.end = cells[i];
14860                 continue;
14861             }
14862             // different row.
14863             rows.push(crow);
14864             crow = {
14865                 start: cells[i],
14866                 end : cells[i]
14867             };
14868             
14869         }
14870         
14871         rows.push(crow);
14872         ev.els = [];
14873         ev.rows = rows;
14874         ev.cells = cells;
14875         
14876         cells[0].events.push(ev);
14877         
14878         this.calevents.push(ev);
14879     },
14880     
14881     clearEvents: function() {
14882         
14883         if(!this.calevents){
14884             return;
14885         }
14886         
14887         Roo.each(this.cells.elements, function(c){
14888             c.row = 0;
14889             c.events = [];
14890             c.more = [];
14891         });
14892         
14893         Roo.each(this.calevents, function(e) {
14894             Roo.each(e.els, function(el) {
14895                 el.un('mouseenter' ,this.onEventEnter, this);
14896                 el.un('mouseleave' ,this.onEventLeave, this);
14897                 el.remove();
14898             },this);
14899         },this);
14900         
14901         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14902             e.remove();
14903         });
14904         
14905     },
14906     
14907     renderEvents: function()
14908     {   
14909         var _this = this;
14910         
14911         this.cells.each(function(c) {
14912             
14913             if(c.row < 5){
14914                 return;
14915             }
14916             
14917             var ev = c.events;
14918             
14919             var r = 4;
14920             if(c.row != c.events.length){
14921                 r = 4 - (4 - (c.row - c.events.length));
14922             }
14923             
14924             c.events = ev.slice(0, r);
14925             c.more = ev.slice(r);
14926             
14927             if(c.more.length && c.more.length == 1){
14928                 c.events.push(c.more.pop());
14929             }
14930             
14931             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14932             
14933         });
14934             
14935         this.cells.each(function(c) {
14936             
14937             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14938             
14939             
14940             for (var e = 0; e < c.events.length; e++){
14941                 var ev = c.events[e];
14942                 var rows = ev.rows;
14943                 
14944                 for(var i = 0; i < rows.length; i++) {
14945                 
14946                     // how many rows should it span..
14947
14948                     var  cfg = {
14949                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14950                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14951
14952                         unselectable : "on",
14953                         cn : [
14954                             {
14955                                 cls: 'fc-event-inner',
14956                                 cn : [
14957     //                                {
14958     //                                  tag:'span',
14959     //                                  cls: 'fc-event-time',
14960     //                                  html : cells.length > 1 ? '' : ev.time
14961     //                                },
14962                                     {
14963                                       tag:'span',
14964                                       cls: 'fc-event-title',
14965                                       html : String.format('{0}', ev.title)
14966                                     }
14967
14968
14969                                 ]
14970                             },
14971                             {
14972                                 cls: 'ui-resizable-handle ui-resizable-e',
14973                                 html : '&nbsp;&nbsp;&nbsp'
14974                             }
14975
14976                         ]
14977                     };
14978
14979                     if (i == 0) {
14980                         cfg.cls += ' fc-event-start';
14981                     }
14982                     if ((i+1) == rows.length) {
14983                         cfg.cls += ' fc-event-end';
14984                     }
14985
14986                     var ctr = _this.el.select('.fc-event-container',true).first();
14987                     var cg = ctr.createChild(cfg);
14988
14989                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14990                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14991
14992                     var r = (c.more.length) ? 1 : 0;
14993                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14994                     cg.setWidth(ebox.right - sbox.x -2);
14995
14996                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14997                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14998                     cg.on('click', _this.onEventClick, _this, ev);
14999
15000                     ev.els.push(cg);
15001                     
15002                 }
15003                 
15004             }
15005             
15006             
15007             if(c.more.length){
15008                 var  cfg = {
15009                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15010                     style : 'position: absolute',
15011                     unselectable : "on",
15012                     cn : [
15013                         {
15014                             cls: 'fc-event-inner',
15015                             cn : [
15016                                 {
15017                                   tag:'span',
15018                                   cls: 'fc-event-title',
15019                                   html : 'More'
15020                                 }
15021
15022
15023                             ]
15024                         },
15025                         {
15026                             cls: 'ui-resizable-handle ui-resizable-e',
15027                             html : '&nbsp;&nbsp;&nbsp'
15028                         }
15029
15030                     ]
15031                 };
15032
15033                 var ctr = _this.el.select('.fc-event-container',true).first();
15034                 var cg = ctr.createChild(cfg);
15035
15036                 var sbox = c.select('.fc-day-content',true).first().getBox();
15037                 var ebox = c.select('.fc-day-content',true).first().getBox();
15038                 //Roo.log(cg);
15039                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15040                 cg.setWidth(ebox.right - sbox.x -2);
15041
15042                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15043                 
15044             }
15045             
15046         });
15047         
15048         
15049         
15050     },
15051     
15052     onEventEnter: function (e, el,event,d) {
15053         this.fireEvent('evententer', this, el, event);
15054     },
15055     
15056     onEventLeave: function (e, el,event,d) {
15057         this.fireEvent('eventleave', this, el, event);
15058     },
15059     
15060     onEventClick: function (e, el,event,d) {
15061         this.fireEvent('eventclick', this, el, event);
15062     },
15063     
15064     onMonthChange: function () {
15065         this.store.load();
15066     },
15067     
15068     onMoreEventClick: function(e, el, more)
15069     {
15070         var _this = this;
15071         
15072         this.calpopover.placement = 'right';
15073         this.calpopover.setTitle('More');
15074         
15075         this.calpopover.setContent('');
15076         
15077         var ctr = this.calpopover.el.select('.popover-content', true).first();
15078         
15079         Roo.each(more, function(m){
15080             var cfg = {
15081                 cls : 'fc-event-hori fc-event-draggable',
15082                 html : m.title
15083             }
15084             var cg = ctr.createChild(cfg);
15085             
15086             cg.on('click', _this.onEventClick, _this, m);
15087         });
15088         
15089         this.calpopover.show(el);
15090         
15091         
15092     },
15093     
15094     onLoad: function () 
15095     {   
15096         this.calevents = [];
15097         var cal = this;
15098         
15099         if(this.store.getCount() > 0){
15100             this.store.data.each(function(d){
15101                cal.addItem({
15102                     id : d.data.id,
15103                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15104                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15105                     time : d.data.start_time,
15106                     title : d.data.title,
15107                     description : d.data.description,
15108                     venue : d.data.venue
15109                 });
15110             });
15111         }
15112         
15113         this.renderEvents();
15114         
15115         if(this.calevents.length && this.loadMask){
15116             this.maskEl.hide();
15117         }
15118     },
15119     
15120     onBeforeLoad: function()
15121     {
15122         this.clearEvents();
15123         if(this.loadMask){
15124             this.maskEl.show();
15125         }
15126     }
15127 });
15128
15129  
15130  /*
15131  * - LGPL
15132  *
15133  * element
15134  * 
15135  */
15136
15137 /**
15138  * @class Roo.bootstrap.Popover
15139  * @extends Roo.bootstrap.Component
15140  * Bootstrap Popover class
15141  * @cfg {String} html contents of the popover   (or false to use children..)
15142  * @cfg {String} title of popover (or false to hide)
15143  * @cfg {String} placement how it is placed
15144  * @cfg {String} trigger click || hover (or false to trigger manually)
15145  * @cfg {String} over what (parent or false to trigger manually.)
15146  * @cfg {Number} delay - delay before showing
15147  
15148  * @constructor
15149  * Create a new Popover
15150  * @param {Object} config The config object
15151  */
15152
15153 Roo.bootstrap.Popover = function(config){
15154     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15155 };
15156
15157 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15158     
15159     title: 'Fill in a title',
15160     html: false,
15161     
15162     placement : 'right',
15163     trigger : 'hover', // hover
15164     
15165     delay : 0,
15166     
15167     over: 'parent',
15168     
15169     can_build_overlaid : false,
15170     
15171     getChildContainer : function()
15172     {
15173         return this.el.select('.popover-content',true).first();
15174     },
15175     
15176     getAutoCreate : function(){
15177          Roo.log('make popover?');
15178         var cfg = {
15179            cls : 'popover roo-dynamic',
15180            style: 'display:block',
15181            cn : [
15182                 {
15183                     cls : 'arrow'
15184                 },
15185                 {
15186                     cls : 'popover-inner',
15187                     cn : [
15188                         {
15189                             tag: 'h3',
15190                             cls: 'popover-title',
15191                             html : this.title
15192                         },
15193                         {
15194                             cls : 'popover-content',
15195                             html : this.html
15196                         }
15197                     ]
15198                     
15199                 }
15200            ]
15201         };
15202         
15203         return cfg;
15204     },
15205     setTitle: function(str)
15206     {
15207         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15208     },
15209     setContent: function(str)
15210     {
15211         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15212     },
15213     // as it get's added to the bottom of the page.
15214     onRender : function(ct, position)
15215     {
15216         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15217         if(!this.el){
15218             var cfg = Roo.apply({},  this.getAutoCreate());
15219             cfg.id = Roo.id();
15220             
15221             if (this.cls) {
15222                 cfg.cls += ' ' + this.cls;
15223             }
15224             if (this.style) {
15225                 cfg.style = this.style;
15226             }
15227             Roo.log("adding to ")
15228             this.el = Roo.get(document.body).createChild(cfg, position);
15229             Roo.log(this.el);
15230         }
15231         this.initEvents();
15232     },
15233     
15234     initEvents : function()
15235     {
15236         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15237         this.el.enableDisplayMode('block');
15238         this.el.hide();
15239         if (this.over === false) {
15240             return; 
15241         }
15242         if (this.triggers === false) {
15243             return;
15244         }
15245         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15246         var triggers = this.trigger ? this.trigger.split(' ') : [];
15247         Roo.each(triggers, function(trigger) {
15248         
15249             if (trigger == 'click') {
15250                 on_el.on('click', this.toggle, this);
15251             } else if (trigger != 'manual') {
15252                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15253                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15254       
15255                 on_el.on(eventIn  ,this.enter, this);
15256                 on_el.on(eventOut, this.leave, this);
15257             }
15258         }, this);
15259         
15260     },
15261     
15262     
15263     // private
15264     timeout : null,
15265     hoverState : null,
15266     
15267     toggle : function () {
15268         this.hoverState == 'in' ? this.leave() : this.enter();
15269     },
15270     
15271     enter : function () {
15272        
15273     
15274         clearTimeout(this.timeout);
15275     
15276         this.hoverState = 'in';
15277     
15278         if (!this.delay || !this.delay.show) {
15279             this.show();
15280             return;
15281         }
15282         var _t = this;
15283         this.timeout = setTimeout(function () {
15284             if (_t.hoverState == 'in') {
15285                 _t.show();
15286             }
15287         }, this.delay.show)
15288     },
15289     leave : function() {
15290         clearTimeout(this.timeout);
15291     
15292         this.hoverState = 'out';
15293     
15294         if (!this.delay || !this.delay.hide) {
15295             this.hide();
15296             return;
15297         }
15298         var _t = this;
15299         this.timeout = setTimeout(function () {
15300             if (_t.hoverState == 'out') {
15301                 _t.hide();
15302             }
15303         }, this.delay.hide)
15304     },
15305     
15306     show : function (on_el)
15307     {
15308         if (!on_el) {
15309             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15310         }
15311         // set content.
15312         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15313         if (this.html !== false) {
15314             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
15315         }
15316         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15317         if (!this.title.length) {
15318             this.el.select('.popover-title',true).hide();
15319         }
15320         
15321         var placement = typeof this.placement == 'function' ?
15322             this.placement.call(this, this.el, on_el) :
15323             this.placement;
15324             
15325         var autoToken = /\s?auto?\s?/i;
15326         var autoPlace = autoToken.test(placement);
15327         if (autoPlace) {
15328             placement = placement.replace(autoToken, '') || 'top';
15329         }
15330         
15331         //this.el.detach()
15332         //this.el.setXY([0,0]);
15333         this.el.show();
15334         this.el.dom.style.display='block';
15335         this.el.addClass(placement);
15336         
15337         //this.el.appendTo(on_el);
15338         
15339         var p = this.getPosition();
15340         var box = this.el.getBox();
15341         
15342         if (autoPlace) {
15343             // fixme..
15344         }
15345         var align = Roo.bootstrap.Popover.alignment[placement];
15346         this.el.alignTo(on_el, align[0],align[1]);
15347         //var arrow = this.el.select('.arrow',true).first();
15348         //arrow.set(align[2], 
15349         
15350         this.el.addClass('in');
15351         this.hoverState = null;
15352         
15353         if (this.el.hasClass('fade')) {
15354             // fade it?
15355         }
15356         
15357     },
15358     hide : function()
15359     {
15360         this.el.setXY([0,0]);
15361         this.el.removeClass('in');
15362         this.el.hide();
15363         
15364     }
15365     
15366 });
15367
15368 Roo.bootstrap.Popover.alignment = {
15369     'left' : ['r-l', [-10,0], 'right'],
15370     'right' : ['l-r', [10,0], 'left'],
15371     'bottom' : ['t-b', [0,10], 'top'],
15372     'top' : [ 'b-t', [0,-10], 'bottom']
15373 };
15374
15375  /*
15376  * - LGPL
15377  *
15378  * Progress
15379  * 
15380  */
15381
15382 /**
15383  * @class Roo.bootstrap.Progress
15384  * @extends Roo.bootstrap.Component
15385  * Bootstrap Progress class
15386  * @cfg {Boolean} striped striped of the progress bar
15387  * @cfg {Boolean} active animated of the progress bar
15388  * 
15389  * 
15390  * @constructor
15391  * Create a new Progress
15392  * @param {Object} config The config object
15393  */
15394
15395 Roo.bootstrap.Progress = function(config){
15396     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15397 };
15398
15399 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15400     
15401     striped : false,
15402     active: false,
15403     
15404     getAutoCreate : function(){
15405         var cfg = {
15406             tag: 'div',
15407             cls: 'progress'
15408         };
15409         
15410         
15411         if(this.striped){
15412             cfg.cls += ' progress-striped';
15413         }
15414       
15415         if(this.active){
15416             cfg.cls += ' active';
15417         }
15418         
15419         
15420         return cfg;
15421     }
15422    
15423 });
15424
15425  
15426
15427  /*
15428  * - LGPL
15429  *
15430  * ProgressBar
15431  * 
15432  */
15433
15434 /**
15435  * @class Roo.bootstrap.ProgressBar
15436  * @extends Roo.bootstrap.Component
15437  * Bootstrap ProgressBar class
15438  * @cfg {Number} aria_valuenow aria-value now
15439  * @cfg {Number} aria_valuemin aria-value min
15440  * @cfg {Number} aria_valuemax aria-value max
15441  * @cfg {String} label label for the progress bar
15442  * @cfg {String} panel (success | info | warning | danger )
15443  * @cfg {String} role role of the progress bar
15444  * @cfg {String} sr_only text
15445  * 
15446  * 
15447  * @constructor
15448  * Create a new ProgressBar
15449  * @param {Object} config The config object
15450  */
15451
15452 Roo.bootstrap.ProgressBar = function(config){
15453     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15454 };
15455
15456 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15457     
15458     aria_valuenow : 0,
15459     aria_valuemin : 0,
15460     aria_valuemax : 100,
15461     label : false,
15462     panel : false,
15463     role : false,
15464     sr_only: false,
15465     
15466     getAutoCreate : function()
15467     {
15468         
15469         var cfg = {
15470             tag: 'div',
15471             cls: 'progress-bar',
15472             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15473         };
15474         
15475         if(this.sr_only){
15476             cfg.cn = {
15477                 tag: 'span',
15478                 cls: 'sr-only',
15479                 html: this.sr_only
15480             }
15481         }
15482         
15483         if(this.role){
15484             cfg.role = this.role;
15485         }
15486         
15487         if(this.aria_valuenow){
15488             cfg['aria-valuenow'] = this.aria_valuenow;
15489         }
15490         
15491         if(this.aria_valuemin){
15492             cfg['aria-valuemin'] = this.aria_valuemin;
15493         }
15494         
15495         if(this.aria_valuemax){
15496             cfg['aria-valuemax'] = this.aria_valuemax;
15497         }
15498         
15499         if(this.label && !this.sr_only){
15500             cfg.html = this.label;
15501         }
15502         
15503         if(this.panel){
15504             cfg.cls += ' progress-bar-' + this.panel;
15505         }
15506         
15507         return cfg;
15508     },
15509     
15510     update : function(aria_valuenow)
15511     {
15512         this.aria_valuenow = aria_valuenow;
15513         
15514         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15515     }
15516    
15517 });
15518
15519  
15520
15521  /*
15522  * - LGPL
15523  *
15524  * column
15525  * 
15526  */
15527
15528 /**
15529  * @class Roo.bootstrap.TabGroup
15530  * @extends Roo.bootstrap.Column
15531  * Bootstrap Column class
15532  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15533  * @cfg {Boolean} carousel true to make the group behave like a carousel
15534  * @cfg {Number} bullets show the panel pointer.. default 0
15535  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15536  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15537  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15538  * 
15539  * @constructor
15540  * Create a new TabGroup
15541  * @param {Object} config The config object
15542  */
15543
15544 Roo.bootstrap.TabGroup = function(config){
15545     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15546     if (!this.navId) {
15547         this.navId = Roo.id();
15548     }
15549     this.tabs = [];
15550     Roo.bootstrap.TabGroup.register(this);
15551     
15552 };
15553
15554 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15555     
15556     carousel : false,
15557     transition : false,
15558     bullets : 0,
15559     timer : 0,
15560     autoslide : false,
15561     slideFn : false,
15562     slideOnTouch : false,
15563     
15564     getAutoCreate : function()
15565     {
15566         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15567         
15568         cfg.cls += ' tab-content';
15569         
15570         Roo.log('get auto create...............');
15571         
15572         if (this.carousel) {
15573             cfg.cls += ' carousel slide';
15574             
15575             cfg.cn = [{
15576                cls : 'carousel-inner'
15577             }];
15578         
15579             if(this.bullets > 0 && !Roo.isTouch){
15580                 
15581                 var bullets = {
15582                     cls : 'carousel-bullets',
15583                     cn : []
15584                 };
15585                 
15586                 if(this.bullets_cls){
15587                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15588                 }
15589                 
15590                 for (var i = 0; i < this.bullets; i++){
15591                     bullets.cn.push({
15592                         cls : 'bullet bullet-' + i
15593                     });
15594                 }
15595                 
15596                 bullets.cn.push({
15597                     cls : 'clear'
15598                 });
15599                 
15600                 cfg.cn[0].cn = bullets;
15601             }
15602         }
15603         
15604         return cfg;
15605     },
15606     
15607     initEvents:  function()
15608     {
15609         Roo.log('-------- init events on tab group ---------');
15610         
15611         if(this.bullets > 0 && !Roo.isTouch){
15612             this.initBullet();
15613         }
15614         
15615         Roo.log(this);
15616         
15617         if(Roo.isTouch && this.slideOnTouch){
15618             this.el.on("touchstart", this.onTouchStart, this);
15619         }
15620         
15621         if(this.autoslide){
15622             var _this = this;
15623             
15624             this.slideFn = window.setInterval(function() {
15625                 _this.showPanelNext();
15626             }, this.timer);
15627         }
15628         
15629     },
15630     
15631     onTouchStart : function(e, el, o)
15632     {
15633         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15634             return;
15635         }
15636         
15637         this.showPanelNext();
15638     },
15639     
15640     getChildContainer : function()
15641     {
15642         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15643     },
15644     
15645     /**
15646     * register a Navigation item
15647     * @param {Roo.bootstrap.NavItem} the navitem to add
15648     */
15649     register : function(item)
15650     {
15651         this.tabs.push( item);
15652         item.navId = this.navId; // not really needed..
15653     
15654     },
15655     
15656     getActivePanel : function()
15657     {
15658         var r = false;
15659         Roo.each(this.tabs, function(t) {
15660             if (t.active) {
15661                 r = t;
15662                 return false;
15663             }
15664             return null;
15665         });
15666         return r;
15667         
15668     },
15669     getPanelByName : function(n)
15670     {
15671         var r = false;
15672         Roo.each(this.tabs, function(t) {
15673             if (t.tabId == n) {
15674                 r = t;
15675                 return false;
15676             }
15677             return null;
15678         });
15679         return r;
15680     },
15681     indexOfPanel : function(p)
15682     {
15683         var r = false;
15684         Roo.each(this.tabs, function(t,i) {
15685             if (t.tabId == p.tabId) {
15686                 r = i;
15687                 return false;
15688             }
15689             return null;
15690         });
15691         return r;
15692     },
15693     /**
15694      * show a specific panel
15695      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15696      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15697      */
15698     showPanel : function (pan)
15699     {
15700         if(this.transition){
15701             Roo.log("waiting for the transitionend");
15702             return;
15703         }
15704         
15705         if (typeof(pan) == 'number') {
15706             pan = this.tabs[pan];
15707         }
15708         if (typeof(pan) == 'string') {
15709             pan = this.getPanelByName(pan);
15710         }
15711         if (pan.tabId == this.getActivePanel().tabId) {
15712             return true;
15713         }
15714         var cur = this.getActivePanel();
15715         
15716         if (false === cur.fireEvent('beforedeactivate')) {
15717             return false;
15718         }
15719         
15720         if(this.bullets > 0 && !Roo.isTouch){
15721             this.setActiveBullet(this.indexOfPanel(pan));
15722         }
15723         
15724         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15725             
15726             this.transition = true;
15727             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15728             var lr = dir == 'next' ? 'left' : 'right';
15729             pan.el.addClass(dir); // or prev
15730             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15731             cur.el.addClass(lr); // or right
15732             pan.el.addClass(lr);
15733             
15734             var _this = this;
15735             cur.el.on('transitionend', function() {
15736                 Roo.log("trans end?");
15737                 
15738                 pan.el.removeClass([lr,dir]);
15739                 pan.setActive(true);
15740                 
15741                 cur.el.removeClass([lr]);
15742                 cur.setActive(false);
15743                 
15744                 _this.transition = false;
15745                 
15746             }, this, { single:  true } );
15747             
15748             return true;
15749         }
15750         
15751         cur.setActive(false);
15752         pan.setActive(true);
15753         
15754         return true;
15755         
15756     },
15757     showPanelNext : function()
15758     {
15759         var i = this.indexOfPanel(this.getActivePanel());
15760         
15761         if (i >= this.tabs.length - 1 && !this.autoslide) {
15762             return;
15763         }
15764         
15765         if (i >= this.tabs.length - 1 && this.autoslide) {
15766             i = -1;
15767         }
15768         
15769         this.showPanel(this.tabs[i+1]);
15770     },
15771     
15772     showPanelPrev : function()
15773     {
15774         var i = this.indexOfPanel(this.getActivePanel());
15775         
15776         if (i  < 1 && !this.autoslide) {
15777             return;
15778         }
15779         
15780         if (i < 1 && this.autoslide) {
15781             i = this.tabs.length;
15782         }
15783         
15784         this.showPanel(this.tabs[i-1]);
15785     },
15786     
15787     initBullet : function()
15788     {
15789         if(Roo.isTouch){
15790             return;
15791         }
15792         
15793         var _this = this;
15794         
15795         for (var i = 0; i < this.bullets; i++){
15796             var bullet = this.el.select('.bullet-' + i, true).first();
15797
15798             if(!bullet){
15799                 continue;
15800             }
15801
15802             bullet.on('click', (function(e, el, o, ii, t){
15803
15804                 e.preventDefault();
15805
15806                 _this.showPanel(ii);
15807
15808                 if(_this.autoslide && _this.slideFn){
15809                     clearInterval(_this.slideFn);
15810                     _this.slideFn = window.setInterval(function() {
15811                         _this.showPanelNext();
15812                     }, _this.timer);
15813                 }
15814
15815             }).createDelegate(this, [i, bullet], true));
15816         }
15817     },
15818     
15819     setActiveBullet : function(i)
15820     {
15821         if(Roo.isTouch){
15822             return;
15823         }
15824         
15825         Roo.each(this.el.select('.bullet', true).elements, function(el){
15826             el.removeClass('selected');
15827         });
15828
15829         var bullet = this.el.select('.bullet-' + i, true).first();
15830         
15831         if(!bullet){
15832             return;
15833         }
15834         
15835         bullet.addClass('selected');
15836     }
15837     
15838     
15839   
15840 });
15841
15842  
15843
15844  
15845  
15846 Roo.apply(Roo.bootstrap.TabGroup, {
15847     
15848     groups: {},
15849      /**
15850     * register a Navigation Group
15851     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15852     */
15853     register : function(navgrp)
15854     {
15855         this.groups[navgrp.navId] = navgrp;
15856         
15857     },
15858     /**
15859     * fetch a Navigation Group based on the navigation ID
15860     * if one does not exist , it will get created.
15861     * @param {string} the navgroup to add
15862     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15863     */
15864     get: function(navId) {
15865         if (typeof(this.groups[navId]) == 'undefined') {
15866             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15867         }
15868         return this.groups[navId] ;
15869     }
15870     
15871     
15872     
15873 });
15874
15875  /*
15876  * - LGPL
15877  *
15878  * TabPanel
15879  * 
15880  */
15881
15882 /**
15883  * @class Roo.bootstrap.TabPanel
15884  * @extends Roo.bootstrap.Component
15885  * Bootstrap TabPanel class
15886  * @cfg {Boolean} active panel active
15887  * @cfg {String} html panel content
15888  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15889  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15890  * 
15891  * 
15892  * @constructor
15893  * Create a new TabPanel
15894  * @param {Object} config The config object
15895  */
15896
15897 Roo.bootstrap.TabPanel = function(config){
15898     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15899     this.addEvents({
15900         /**
15901              * @event changed
15902              * Fires when the active status changes
15903              * @param {Roo.bootstrap.TabPanel} this
15904              * @param {Boolean} state the new state
15905             
15906          */
15907         'changed': true,
15908         /**
15909              * @event beforedeactivate
15910              * Fires before a tab is de-activated - can be used to do validation on a form.
15911              * @param {Roo.bootstrap.TabPanel} this
15912              * @return {Boolean} false if there is an error
15913             
15914          */
15915         'beforedeactivate': true
15916      });
15917     
15918     this.tabId = this.tabId || Roo.id();
15919   
15920 };
15921
15922 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15923     
15924     active: false,
15925     html: false,
15926     tabId: false,
15927     navId : false,
15928     
15929     getAutoCreate : function(){
15930         var cfg = {
15931             tag: 'div',
15932             // item is needed for carousel - not sure if it has any effect otherwise
15933             cls: 'tab-pane item',
15934             html: this.html || ''
15935         };
15936         
15937         if(this.active){
15938             cfg.cls += ' active';
15939         }
15940         
15941         if(this.tabId){
15942             cfg.tabId = this.tabId;
15943         }
15944         
15945         
15946         return cfg;
15947     },
15948     
15949     initEvents:  function()
15950     {
15951         Roo.log('-------- init events on tab panel ---------');
15952         
15953         var p = this.parent();
15954         this.navId = this.navId || p.navId;
15955         
15956         if (typeof(this.navId) != 'undefined') {
15957             // not really needed.. but just in case.. parent should be a NavGroup.
15958             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15959             Roo.log(['register', tg, this]);
15960             tg.register(this);
15961             
15962             var i = tg.tabs.length - 1;
15963             
15964             if(this.active && tg.bullets > 0 && i < tg.bullets){
15965                 tg.setActiveBullet(i);
15966             }
15967         }
15968         
15969     },
15970     
15971     
15972     onRender : function(ct, position)
15973     {
15974        // Roo.log("Call onRender: " + this.xtype);
15975         
15976         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15977         
15978         
15979         
15980         
15981         
15982     },
15983     
15984     setActive: function(state)
15985     {
15986         Roo.log("panel - set active " + this.tabId + "=" + state);
15987         
15988         this.active = state;
15989         if (!state) {
15990             this.el.removeClass('active');
15991             
15992         } else  if (!this.el.hasClass('active')) {
15993             this.el.addClass('active');
15994         }
15995         
15996         this.fireEvent('changed', this, state);
15997     }
15998     
15999     
16000 });
16001  
16002
16003  
16004
16005  /*
16006  * - LGPL
16007  *
16008  * DateField
16009  * 
16010  */
16011
16012 /**
16013  * @class Roo.bootstrap.DateField
16014  * @extends Roo.bootstrap.Input
16015  * Bootstrap DateField class
16016  * @cfg {Number} weekStart default 0
16017  * @cfg {String} viewMode default empty, (months|years)
16018  * @cfg {String} minViewMode default empty, (months|years)
16019  * @cfg {Number} startDate default -Infinity
16020  * @cfg {Number} endDate default Infinity
16021  * @cfg {Boolean} todayHighlight default false
16022  * @cfg {Boolean} todayBtn default false
16023  * @cfg {Boolean} calendarWeeks default false
16024  * @cfg {Object} daysOfWeekDisabled default empty
16025  * @cfg {Boolean} singleMode default false (true | false)
16026  * 
16027  * @cfg {Boolean} keyboardNavigation default true
16028  * @cfg {String} language default en
16029  * 
16030  * @constructor
16031  * Create a new DateField
16032  * @param {Object} config The config object
16033  */
16034
16035 Roo.bootstrap.DateField = function(config){
16036     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16037      this.addEvents({
16038             /**
16039              * @event show
16040              * Fires when this field show.
16041              * @param {Roo.bootstrap.DateField} this
16042              * @param {Mixed} date The date value
16043              */
16044             show : true,
16045             /**
16046              * @event show
16047              * Fires when this field hide.
16048              * @param {Roo.bootstrap.DateField} this
16049              * @param {Mixed} date The date value
16050              */
16051             hide : true,
16052             /**
16053              * @event select
16054              * Fires when select a date.
16055              * @param {Roo.bootstrap.DateField} this
16056              * @param {Mixed} date The date value
16057              */
16058             select : true
16059         });
16060 };
16061
16062 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16063     
16064     /**
16065      * @cfg {String} format
16066      * The default date format string which can be overriden for localization support.  The format must be
16067      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16068      */
16069     format : "m/d/y",
16070     /**
16071      * @cfg {String} altFormats
16072      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16073      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16074      */
16075     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16076     
16077     weekStart : 0,
16078     
16079     viewMode : '',
16080     
16081     minViewMode : '',
16082     
16083     todayHighlight : false,
16084     
16085     todayBtn: false,
16086     
16087     language: 'en',
16088     
16089     keyboardNavigation: true,
16090     
16091     calendarWeeks: false,
16092     
16093     startDate: -Infinity,
16094     
16095     endDate: Infinity,
16096     
16097     daysOfWeekDisabled: [],
16098     
16099     _events: [],
16100     
16101     singleMode : false,
16102     
16103     UTCDate: function()
16104     {
16105         return new Date(Date.UTC.apply(Date, arguments));
16106     },
16107     
16108     UTCToday: function()
16109     {
16110         var today = new Date();
16111         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16112     },
16113     
16114     getDate: function() {
16115             var d = this.getUTCDate();
16116             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16117     },
16118     
16119     getUTCDate: function() {
16120             return this.date;
16121     },
16122     
16123     setDate: function(d) {
16124             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16125     },
16126     
16127     setUTCDate: function(d) {
16128             this.date = d;
16129             this.setValue(this.formatDate(this.date));
16130     },
16131         
16132     onRender: function(ct, position)
16133     {
16134         
16135         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16136         
16137         this.language = this.language || 'en';
16138         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16140         
16141         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16142         this.format = this.format || 'm/d/y';
16143         this.isInline = false;
16144         this.isInput = true;
16145         this.component = this.el.select('.add-on', true).first() || false;
16146         this.component = (this.component && this.component.length === 0) ? false : this.component;
16147         this.hasInput = this.component && this.inputEL().length;
16148         
16149         if (typeof(this.minViewMode === 'string')) {
16150             switch (this.minViewMode) {
16151                 case 'months':
16152                     this.minViewMode = 1;
16153                     break;
16154                 case 'years':
16155                     this.minViewMode = 2;
16156                     break;
16157                 default:
16158                     this.minViewMode = 0;
16159                     break;
16160             }
16161         }
16162         
16163         if (typeof(this.viewMode === 'string')) {
16164             switch (this.viewMode) {
16165                 case 'months':
16166                     this.viewMode = 1;
16167                     break;
16168                 case 'years':
16169                     this.viewMode = 2;
16170                     break;
16171                 default:
16172                     this.viewMode = 0;
16173                     break;
16174             }
16175         }
16176                 
16177         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16178         
16179 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16180         
16181         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16182         
16183         this.picker().on('mousedown', this.onMousedown, this);
16184         this.picker().on('click', this.onClick, this);
16185         
16186         this.picker().addClass('datepicker-dropdown');
16187         
16188         this.startViewMode = this.viewMode;
16189         
16190         if(this.singleMode){
16191             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16192                 v.setVisibilityMode(Roo.Element.DISPLAY)
16193                 v.hide();
16194             });
16195             
16196             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16197                 v.setStyle('width', '189px');
16198             });
16199         }
16200         
16201         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16202             if(!this.calendarWeeks){
16203                 v.remove();
16204                 return;
16205             }
16206             
16207             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16208             v.attr('colspan', function(i, val){
16209                 return parseInt(val) + 1;
16210             });
16211         })
16212                         
16213         
16214         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16215         
16216         this.setStartDate(this.startDate);
16217         this.setEndDate(this.endDate);
16218         
16219         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16220         
16221         this.fillDow();
16222         this.fillMonths();
16223         this.update();
16224         this.showMode();
16225         
16226         if(this.isInline) {
16227             this.show();
16228         }
16229     },
16230     
16231     picker : function()
16232     {
16233         return this.pickerEl;
16234 //        return this.el.select('.datepicker', true).first();
16235     },
16236     
16237     fillDow: function()
16238     {
16239         var dowCnt = this.weekStart;
16240         
16241         var dow = {
16242             tag: 'tr',
16243             cn: [
16244                 
16245             ]
16246         };
16247         
16248         if(this.calendarWeeks){
16249             dow.cn.push({
16250                 tag: 'th',
16251                 cls: 'cw',
16252                 html: '&nbsp;'
16253             })
16254         }
16255         
16256         while (dowCnt < this.weekStart + 7) {
16257             dow.cn.push({
16258                 tag: 'th',
16259                 cls: 'dow',
16260                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16261             });
16262         }
16263         
16264         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16265     },
16266     
16267     fillMonths: function()
16268     {    
16269         var i = 0;
16270         var months = this.picker().select('>.datepicker-months td', true).first();
16271         
16272         months.dom.innerHTML = '';
16273         
16274         while (i < 12) {
16275             var month = {
16276                 tag: 'span',
16277                 cls: 'month',
16278                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16279             }
16280             
16281             months.createChild(month);
16282         }
16283         
16284     },
16285     
16286     update: function()
16287     {
16288         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16289         
16290         if (this.date < this.startDate) {
16291             this.viewDate = new Date(this.startDate);
16292         } else if (this.date > this.endDate) {
16293             this.viewDate = new Date(this.endDate);
16294         } else {
16295             this.viewDate = new Date(this.date);
16296         }
16297         
16298         this.fill();
16299     },
16300     
16301     fill: function() 
16302     {
16303         var d = new Date(this.viewDate),
16304                 year = d.getUTCFullYear(),
16305                 month = d.getUTCMonth(),
16306                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16307                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16308                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16309                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16310                 currentDate = this.date && this.date.valueOf(),
16311                 today = this.UTCToday();
16312         
16313         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16314         
16315 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16316         
16317 //        this.picker.select('>tfoot th.today').
16318 //                                              .text(dates[this.language].today)
16319 //                                              .toggle(this.todayBtn !== false);
16320     
16321         this.updateNavArrows();
16322         this.fillMonths();
16323                                                 
16324         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16325         
16326         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16327          
16328         prevMonth.setUTCDate(day);
16329         
16330         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16331         
16332         var nextMonth = new Date(prevMonth);
16333         
16334         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16335         
16336         nextMonth = nextMonth.valueOf();
16337         
16338         var fillMonths = false;
16339         
16340         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16341         
16342         while(prevMonth.valueOf() < nextMonth) {
16343             var clsName = '';
16344             
16345             if (prevMonth.getUTCDay() === this.weekStart) {
16346                 if(fillMonths){
16347                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16348                 }
16349                     
16350                 fillMonths = {
16351                     tag: 'tr',
16352                     cn: []
16353                 };
16354                 
16355                 if(this.calendarWeeks){
16356                     // ISO 8601: First week contains first thursday.
16357                     // ISO also states week starts on Monday, but we can be more abstract here.
16358                     var
16359                     // Start of current week: based on weekstart/current date
16360                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16361                     // Thursday of this week
16362                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16363                     // First Thursday of year, year from thursday
16364                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16365                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16366                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16367                     
16368                     fillMonths.cn.push({
16369                         tag: 'td',
16370                         cls: 'cw',
16371                         html: calWeek
16372                     });
16373                 }
16374             }
16375             
16376             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16377                 clsName += ' old';
16378             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16379                 clsName += ' new';
16380             }
16381             if (this.todayHighlight &&
16382                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16383                 prevMonth.getUTCMonth() == today.getMonth() &&
16384                 prevMonth.getUTCDate() == today.getDate()) {
16385                 clsName += ' today';
16386             }
16387             
16388             if (currentDate && prevMonth.valueOf() === currentDate) {
16389                 clsName += ' active';
16390             }
16391             
16392             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16393                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16394                     clsName += ' disabled';
16395             }
16396             
16397             fillMonths.cn.push({
16398                 tag: 'td',
16399                 cls: 'day ' + clsName,
16400                 html: prevMonth.getDate()
16401             })
16402             
16403             prevMonth.setDate(prevMonth.getDate()+1);
16404         }
16405           
16406         var currentYear = this.date && this.date.getUTCFullYear();
16407         var currentMonth = this.date && this.date.getUTCMonth();
16408         
16409         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16410         
16411         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16412             v.removeClass('active');
16413             
16414             if(currentYear === year && k === currentMonth){
16415                 v.addClass('active');
16416             }
16417             
16418             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16419                 v.addClass('disabled');
16420             }
16421             
16422         });
16423         
16424         
16425         year = parseInt(year/10, 10) * 10;
16426         
16427         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16428         
16429         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16430         
16431         year -= 1;
16432         for (var i = -1; i < 11; i++) {
16433             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16434                 tag: 'span',
16435                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16436                 html: year
16437             })
16438             
16439             year += 1;
16440         }
16441     },
16442     
16443     showMode: function(dir) 
16444     {
16445         if (dir) {
16446             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16447         }
16448         
16449         Roo.each(this.picker().select('>div',true).elements, function(v){
16450             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16451             v.hide();
16452         });
16453         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16454     },
16455     
16456     place: function()
16457     {
16458         if(this.isInline) return;
16459         
16460         this.picker().removeClass(['bottom', 'top']);
16461         
16462         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16463             /*
16464              * place to the top of element!
16465              *
16466              */
16467             
16468             this.picker().addClass('top');
16469             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16470             
16471             return;
16472         }
16473         
16474         this.picker().addClass('bottom');
16475         
16476         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16477     },
16478     
16479     parseDate : function(value)
16480     {
16481         if(!value || value instanceof Date){
16482             return value;
16483         }
16484         var v = Date.parseDate(value, this.format);
16485         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16486             v = Date.parseDate(value, 'Y-m-d');
16487         }
16488         if(!v && this.altFormats){
16489             if(!this.altFormatsArray){
16490                 this.altFormatsArray = this.altFormats.split("|");
16491             }
16492             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16493                 v = Date.parseDate(value, this.altFormatsArray[i]);
16494             }
16495         }
16496         return v;
16497     },
16498     
16499     formatDate : function(date, fmt)
16500     {   
16501         return (!date || !(date instanceof Date)) ?
16502         date : date.dateFormat(fmt || this.format);
16503     },
16504     
16505     onFocus : function()
16506     {
16507         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16508         this.show();
16509     },
16510     
16511     onBlur : function()
16512     {
16513         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16514         
16515         var d = this.inputEl().getValue();
16516         
16517         this.setValue(d);
16518                 
16519         this.hide();
16520     },
16521     
16522     show : function()
16523     {
16524         this.picker().show();
16525         this.update();
16526         this.place();
16527         
16528         this.fireEvent('show', this, this.date);
16529     },
16530     
16531     hide : function()
16532     {
16533         if(this.isInline) return;
16534         this.picker().hide();
16535         this.viewMode = this.startViewMode;
16536         this.showMode();
16537         
16538         this.fireEvent('hide', this, this.date);
16539         
16540     },
16541     
16542     onMousedown: function(e)
16543     {
16544         e.stopPropagation();
16545         e.preventDefault();
16546     },
16547     
16548     keyup: function(e)
16549     {
16550         Roo.bootstrap.DateField.superclass.keyup.call(this);
16551         this.update();
16552     },
16553
16554     setValue: function(v)
16555     {
16556         
16557         // v can be a string or a date..
16558         
16559         
16560         var d = new Date(this.parseDate(v) ).clearTime();
16561         
16562         if(isNaN(d.getTime())){
16563             this.date = this.viewDate = '';
16564             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16565             return;
16566         }
16567         
16568         v = this.formatDate(d);
16569         
16570         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16571         
16572         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16573      
16574         this.update();
16575
16576         this.fireEvent('select', this, this.date);
16577         
16578     },
16579     
16580     getValue: function()
16581     {
16582         return this.formatDate(this.date);
16583     },
16584     
16585     fireKey: function(e)
16586     {
16587         if (!this.picker().isVisible()){
16588             if (e.keyCode == 27) // allow escape to hide and re-show picker
16589                 this.show();
16590             return;
16591         }
16592         
16593         var dateChanged = false,
16594         dir, day, month,
16595         newDate, newViewDate;
16596         
16597         switch(e.keyCode){
16598             case 27: // escape
16599                 this.hide();
16600                 e.preventDefault();
16601                 break;
16602             case 37: // left
16603             case 39: // right
16604                 if (!this.keyboardNavigation) break;
16605                 dir = e.keyCode == 37 ? -1 : 1;
16606                 
16607                 if (e.ctrlKey){
16608                     newDate = this.moveYear(this.date, dir);
16609                     newViewDate = this.moveYear(this.viewDate, dir);
16610                 } else if (e.shiftKey){
16611                     newDate = this.moveMonth(this.date, dir);
16612                     newViewDate = this.moveMonth(this.viewDate, dir);
16613                 } else {
16614                     newDate = new Date(this.date);
16615                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16616                     newViewDate = new Date(this.viewDate);
16617                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16618                 }
16619                 if (this.dateWithinRange(newDate)){
16620                     this.date = newDate;
16621                     this.viewDate = newViewDate;
16622                     this.setValue(this.formatDate(this.date));
16623 //                    this.update();
16624                     e.preventDefault();
16625                     dateChanged = true;
16626                 }
16627                 break;
16628             case 38: // up
16629             case 40: // down
16630                 if (!this.keyboardNavigation) break;
16631                 dir = e.keyCode == 38 ? -1 : 1;
16632                 if (e.ctrlKey){
16633                     newDate = this.moveYear(this.date, dir);
16634                     newViewDate = this.moveYear(this.viewDate, dir);
16635                 } else if (e.shiftKey){
16636                     newDate = this.moveMonth(this.date, dir);
16637                     newViewDate = this.moveMonth(this.viewDate, dir);
16638                 } else {
16639                     newDate = new Date(this.date);
16640                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16641                     newViewDate = new Date(this.viewDate);
16642                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16643                 }
16644                 if (this.dateWithinRange(newDate)){
16645                     this.date = newDate;
16646                     this.viewDate = newViewDate;
16647                     this.setValue(this.formatDate(this.date));
16648 //                    this.update();
16649                     e.preventDefault();
16650                     dateChanged = true;
16651                 }
16652                 break;
16653             case 13: // enter
16654                 this.setValue(this.formatDate(this.date));
16655                 this.hide();
16656                 e.preventDefault();
16657                 break;
16658             case 9: // tab
16659                 this.setValue(this.formatDate(this.date));
16660                 this.hide();
16661                 break;
16662             case 16: // shift
16663             case 17: // ctrl
16664             case 18: // alt
16665                 break;
16666             default :
16667                 this.hide();
16668                 
16669         }
16670     },
16671     
16672     
16673     onClick: function(e) 
16674     {
16675         e.stopPropagation();
16676         e.preventDefault();
16677         
16678         var target = e.getTarget();
16679         
16680         if(target.nodeName.toLowerCase() === 'i'){
16681             target = Roo.get(target).dom.parentNode;
16682         }
16683         
16684         var nodeName = target.nodeName;
16685         var className = target.className;
16686         var html = target.innerHTML;
16687         //Roo.log(nodeName);
16688         
16689         switch(nodeName.toLowerCase()) {
16690             case 'th':
16691                 switch(className) {
16692                     case 'switch':
16693                         this.showMode(1);
16694                         break;
16695                     case 'prev':
16696                     case 'next':
16697                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16698                         switch(this.viewMode){
16699                                 case 0:
16700                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16701                                         break;
16702                                 case 1:
16703                                 case 2:
16704                                         this.viewDate = this.moveYear(this.viewDate, dir);
16705                                         break;
16706                         }
16707                         this.fill();
16708                         break;
16709                     case 'today':
16710                         var date = new Date();
16711                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16712 //                        this.fill()
16713                         this.setValue(this.formatDate(this.date));
16714                         
16715                         this.hide();
16716                         break;
16717                 }
16718                 break;
16719             case 'span':
16720                 if (className.indexOf('disabled') < 0) {
16721                     this.viewDate.setUTCDate(1);
16722                     if (className.indexOf('month') > -1) {
16723                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16724                     } else {
16725                         var year = parseInt(html, 10) || 0;
16726                         this.viewDate.setUTCFullYear(year);
16727                         
16728                     }
16729                     
16730                     if(this.singleMode){
16731                         this.setValue(this.formatDate(this.viewDate));
16732                         this.hide();
16733                         return;
16734                     }
16735                     
16736                     this.showMode(-1);
16737                     this.fill();
16738                 }
16739                 break;
16740                 
16741             case 'td':
16742                 //Roo.log(className);
16743                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16744                     var day = parseInt(html, 10) || 1;
16745                     var year = this.viewDate.getUTCFullYear(),
16746                         month = this.viewDate.getUTCMonth();
16747
16748                     if (className.indexOf('old') > -1) {
16749                         if(month === 0 ){
16750                             month = 11;
16751                             year -= 1;
16752                         }else{
16753                             month -= 1;
16754                         }
16755                     } else if (className.indexOf('new') > -1) {
16756                         if (month == 11) {
16757                             month = 0;
16758                             year += 1;
16759                         } else {
16760                             month += 1;
16761                         }
16762                     }
16763                     //Roo.log([year,month,day]);
16764                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16765                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16766 //                    this.fill();
16767                     //Roo.log(this.formatDate(this.date));
16768                     this.setValue(this.formatDate(this.date));
16769                     this.hide();
16770                 }
16771                 break;
16772         }
16773     },
16774     
16775     setStartDate: function(startDate)
16776     {
16777         this.startDate = startDate || -Infinity;
16778         if (this.startDate !== -Infinity) {
16779             this.startDate = this.parseDate(this.startDate);
16780         }
16781         this.update();
16782         this.updateNavArrows();
16783     },
16784
16785     setEndDate: function(endDate)
16786     {
16787         this.endDate = endDate || Infinity;
16788         if (this.endDate !== Infinity) {
16789             this.endDate = this.parseDate(this.endDate);
16790         }
16791         this.update();
16792         this.updateNavArrows();
16793     },
16794     
16795     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16796     {
16797         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16798         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16799             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16800         }
16801         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16802             return parseInt(d, 10);
16803         });
16804         this.update();
16805         this.updateNavArrows();
16806     },
16807     
16808     updateNavArrows: function() 
16809     {
16810         if(this.singleMode){
16811             return;
16812         }
16813         
16814         var d = new Date(this.viewDate),
16815         year = d.getUTCFullYear(),
16816         month = d.getUTCMonth();
16817         
16818         Roo.each(this.picker().select('.prev', true).elements, function(v){
16819             v.show();
16820             switch (this.viewMode) {
16821                 case 0:
16822
16823                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16824                         v.hide();
16825                     }
16826                     break;
16827                 case 1:
16828                 case 2:
16829                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16830                         v.hide();
16831                     }
16832                     break;
16833             }
16834         });
16835         
16836         Roo.each(this.picker().select('.next', true).elements, function(v){
16837             v.show();
16838             switch (this.viewMode) {
16839                 case 0:
16840
16841                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16842                         v.hide();
16843                     }
16844                     break;
16845                 case 1:
16846                 case 2:
16847                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16848                         v.hide();
16849                     }
16850                     break;
16851             }
16852         })
16853     },
16854     
16855     moveMonth: function(date, dir)
16856     {
16857         if (!dir) return date;
16858         var new_date = new Date(date.valueOf()),
16859         day = new_date.getUTCDate(),
16860         month = new_date.getUTCMonth(),
16861         mag = Math.abs(dir),
16862         new_month, test;
16863         dir = dir > 0 ? 1 : -1;
16864         if (mag == 1){
16865             test = dir == -1
16866             // If going back one month, make sure month is not current month
16867             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16868             ? function(){
16869                 return new_date.getUTCMonth() == month;
16870             }
16871             // If going forward one month, make sure month is as expected
16872             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16873             : function(){
16874                 return new_date.getUTCMonth() != new_month;
16875             };
16876             new_month = month + dir;
16877             new_date.setUTCMonth(new_month);
16878             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16879             if (new_month < 0 || new_month > 11)
16880                 new_month = (new_month + 12) % 12;
16881         } else {
16882             // For magnitudes >1, move one month at a time...
16883             for (var i=0; i<mag; i++)
16884                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16885                 new_date = this.moveMonth(new_date, dir);
16886             // ...then reset the day, keeping it in the new month
16887             new_month = new_date.getUTCMonth();
16888             new_date.setUTCDate(day);
16889             test = function(){
16890                 return new_month != new_date.getUTCMonth();
16891             };
16892         }
16893         // Common date-resetting loop -- if date is beyond end of month, make it
16894         // end of month
16895         while (test()){
16896             new_date.setUTCDate(--day);
16897             new_date.setUTCMonth(new_month);
16898         }
16899         return new_date;
16900     },
16901
16902     moveYear: function(date, dir)
16903     {
16904         return this.moveMonth(date, dir*12);
16905     },
16906
16907     dateWithinRange: function(date)
16908     {
16909         return date >= this.startDate && date <= this.endDate;
16910     },
16911
16912     
16913     remove: function() 
16914     {
16915         this.picker().remove();
16916     }
16917    
16918 });
16919
16920 Roo.apply(Roo.bootstrap.DateField,  {
16921     
16922     head : {
16923         tag: 'thead',
16924         cn: [
16925         {
16926             tag: 'tr',
16927             cn: [
16928             {
16929                 tag: 'th',
16930                 cls: 'prev',
16931                 html: '<i class="fa fa-arrow-left"/>'
16932             },
16933             {
16934                 tag: 'th',
16935                 cls: 'switch',
16936                 colspan: '5'
16937             },
16938             {
16939                 tag: 'th',
16940                 cls: 'next',
16941                 html: '<i class="fa fa-arrow-right"/>'
16942             }
16943
16944             ]
16945         }
16946         ]
16947     },
16948     
16949     content : {
16950         tag: 'tbody',
16951         cn: [
16952         {
16953             tag: 'tr',
16954             cn: [
16955             {
16956                 tag: 'td',
16957                 colspan: '7'
16958             }
16959             ]
16960         }
16961         ]
16962     },
16963     
16964     footer : {
16965         tag: 'tfoot',
16966         cn: [
16967         {
16968             tag: 'tr',
16969             cn: [
16970             {
16971                 tag: 'th',
16972                 colspan: '7',
16973                 cls: 'today'
16974             }
16975                     
16976             ]
16977         }
16978         ]
16979     },
16980     
16981     dates:{
16982         en: {
16983             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16984             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16985             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16986             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16987             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16988             today: "Today"
16989         }
16990     },
16991     
16992     modes: [
16993     {
16994         clsName: 'days',
16995         navFnc: 'Month',
16996         navStep: 1
16997     },
16998     {
16999         clsName: 'months',
17000         navFnc: 'FullYear',
17001         navStep: 1
17002     },
17003     {
17004         clsName: 'years',
17005         navFnc: 'FullYear',
17006         navStep: 10
17007     }]
17008 });
17009
17010 Roo.apply(Roo.bootstrap.DateField,  {
17011   
17012     template : {
17013         tag: 'div',
17014         cls: 'datepicker dropdown-menu roo-dynamic',
17015         cn: [
17016         {
17017             tag: 'div',
17018             cls: 'datepicker-days',
17019             cn: [
17020             {
17021                 tag: 'table',
17022                 cls: 'table-condensed',
17023                 cn:[
17024                 Roo.bootstrap.DateField.head,
17025                 {
17026                     tag: 'tbody'
17027                 },
17028                 Roo.bootstrap.DateField.footer
17029                 ]
17030             }
17031             ]
17032         },
17033         {
17034             tag: 'div',
17035             cls: 'datepicker-months',
17036             cn: [
17037             {
17038                 tag: 'table',
17039                 cls: 'table-condensed',
17040                 cn:[
17041                 Roo.bootstrap.DateField.head,
17042                 Roo.bootstrap.DateField.content,
17043                 Roo.bootstrap.DateField.footer
17044                 ]
17045             }
17046             ]
17047         },
17048         {
17049             tag: 'div',
17050             cls: 'datepicker-years',
17051             cn: [
17052             {
17053                 tag: 'table',
17054                 cls: 'table-condensed',
17055                 cn:[
17056                 Roo.bootstrap.DateField.head,
17057                 Roo.bootstrap.DateField.content,
17058                 Roo.bootstrap.DateField.footer
17059                 ]
17060             }
17061             ]
17062         }
17063         ]
17064     }
17065 });
17066
17067  
17068
17069  /*
17070  * - LGPL
17071  *
17072  * TimeField
17073  * 
17074  */
17075
17076 /**
17077  * @class Roo.bootstrap.TimeField
17078  * @extends Roo.bootstrap.Input
17079  * Bootstrap DateField class
17080  * 
17081  * 
17082  * @constructor
17083  * Create a new TimeField
17084  * @param {Object} config The config object
17085  */
17086
17087 Roo.bootstrap.TimeField = function(config){
17088     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17089     this.addEvents({
17090             /**
17091              * @event show
17092              * Fires when this field show.
17093              * @param {Roo.bootstrap.DateField} thisthis
17094              * @param {Mixed} date The date value
17095              */
17096             show : true,
17097             /**
17098              * @event show
17099              * Fires when this field hide.
17100              * @param {Roo.bootstrap.DateField} this
17101              * @param {Mixed} date The date value
17102              */
17103             hide : true,
17104             /**
17105              * @event select
17106              * Fires when select a date.
17107              * @param {Roo.bootstrap.DateField} this
17108              * @param {Mixed} date The date value
17109              */
17110             select : true
17111         });
17112 };
17113
17114 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17115     
17116     /**
17117      * @cfg {String} format
17118      * The default time format string which can be overriden for localization support.  The format must be
17119      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17120      */
17121     format : "H:i",
17122        
17123     onRender: function(ct, position)
17124     {
17125         
17126         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17127                 
17128         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17129         
17130         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17131         
17132         this.pop = this.picker().select('>.datepicker-time',true).first();
17133         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17134         
17135         this.picker().on('mousedown', this.onMousedown, this);
17136         this.picker().on('click', this.onClick, this);
17137         
17138         this.picker().addClass('datepicker-dropdown');
17139     
17140         this.fillTime();
17141         this.update();
17142             
17143         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17144         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17145         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17146         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17147         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17148         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17149
17150     },
17151     
17152     fireKey: function(e){
17153         if (!this.picker().isVisible()){
17154             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17155                 this.show();
17156             }
17157             return;
17158         }
17159
17160         e.preventDefault();
17161         
17162         switch(e.keyCode){
17163             case 27: // escape
17164                 this.hide();
17165                 break;
17166             case 37: // left
17167             case 39: // right
17168                 this.onTogglePeriod();
17169                 break;
17170             case 38: // up
17171                 this.onIncrementMinutes();
17172                 break;
17173             case 40: // down
17174                 this.onDecrementMinutes();
17175                 break;
17176             case 13: // enter
17177             case 9: // tab
17178                 this.setTime();
17179                 break;
17180         }
17181     },
17182     
17183     onClick: function(e) {
17184         e.stopPropagation();
17185         e.preventDefault();
17186     },
17187     
17188     picker : function()
17189     {
17190         return this.el.select('.datepicker', true).first();
17191     },
17192     
17193     fillTime: function()
17194     {    
17195         var time = this.pop.select('tbody', true).first();
17196         
17197         time.dom.innerHTML = '';
17198         
17199         time.createChild({
17200             tag: 'tr',
17201             cn: [
17202                 {
17203                     tag: 'td',
17204                     cn: [
17205                         {
17206                             tag: 'a',
17207                             href: '#',
17208                             cls: 'btn',
17209                             cn: [
17210                                 {
17211                                     tag: 'span',
17212                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17213                                 }
17214                             ]
17215                         } 
17216                     ]
17217                 },
17218                 {
17219                     tag: 'td',
17220                     cls: 'separator'
17221                 },
17222                 {
17223                     tag: 'td',
17224                     cn: [
17225                         {
17226                             tag: 'a',
17227                             href: '#',
17228                             cls: 'btn',
17229                             cn: [
17230                                 {
17231                                     tag: 'span',
17232                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17233                                 }
17234                             ]
17235                         }
17236                     ]
17237                 },
17238                 {
17239                     tag: 'td',
17240                     cls: 'separator'
17241                 }
17242             ]
17243         });
17244         
17245         time.createChild({
17246             tag: 'tr',
17247             cn: [
17248                 {
17249                     tag: 'td',
17250                     cn: [
17251                         {
17252                             tag: 'span',
17253                             cls: 'timepicker-hour',
17254                             html: '00'
17255                         }  
17256                     ]
17257                 },
17258                 {
17259                     tag: 'td',
17260                     cls: 'separator',
17261                     html: ':'
17262                 },
17263                 {
17264                     tag: 'td',
17265                     cn: [
17266                         {
17267                             tag: 'span',
17268                             cls: 'timepicker-minute',
17269                             html: '00'
17270                         }  
17271                     ]
17272                 },
17273                 {
17274                     tag: 'td',
17275                     cls: 'separator'
17276                 },
17277                 {
17278                     tag: 'td',
17279                     cn: [
17280                         {
17281                             tag: 'button',
17282                             type: 'button',
17283                             cls: 'btn btn-primary period',
17284                             html: 'AM'
17285                             
17286                         }
17287                     ]
17288                 }
17289             ]
17290         });
17291         
17292         time.createChild({
17293             tag: 'tr',
17294             cn: [
17295                 {
17296                     tag: 'td',
17297                     cn: [
17298                         {
17299                             tag: 'a',
17300                             href: '#',
17301                             cls: 'btn',
17302                             cn: [
17303                                 {
17304                                     tag: 'span',
17305                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17306                                 }
17307                             ]
17308                         }
17309                     ]
17310                 },
17311                 {
17312                     tag: 'td',
17313                     cls: 'separator'
17314                 },
17315                 {
17316                     tag: 'td',
17317                     cn: [
17318                         {
17319                             tag: 'a',
17320                             href: '#',
17321                             cls: 'btn',
17322                             cn: [
17323                                 {
17324                                     tag: 'span',
17325                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17326                                 }
17327                             ]
17328                         }
17329                     ]
17330                 },
17331                 {
17332                     tag: 'td',
17333                     cls: 'separator'
17334                 }
17335             ]
17336         });
17337         
17338     },
17339     
17340     update: function()
17341     {
17342         
17343         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17344         
17345         this.fill();
17346     },
17347     
17348     fill: function() 
17349     {
17350         var hours = this.time.getHours();
17351         var minutes = this.time.getMinutes();
17352         var period = 'AM';
17353         
17354         if(hours > 11){
17355             period = 'PM';
17356         }
17357         
17358         if(hours == 0){
17359             hours = 12;
17360         }
17361         
17362         
17363         if(hours > 12){
17364             hours = hours - 12;
17365         }
17366         
17367         if(hours < 10){
17368             hours = '0' + hours;
17369         }
17370         
17371         if(minutes < 10){
17372             minutes = '0' + minutes;
17373         }
17374         
17375         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17376         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17377         this.pop.select('button', true).first().dom.innerHTML = period;
17378         
17379     },
17380     
17381     place: function()
17382     {   
17383         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17384         
17385         var cls = ['bottom'];
17386         
17387         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17388             cls.pop();
17389             cls.push('top');
17390         }
17391         
17392         cls.push('right');
17393         
17394         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17395             cls.pop();
17396             cls.push('left');
17397         }
17398         
17399         this.picker().addClass(cls.join('-'));
17400         
17401         var _this = this;
17402         
17403         Roo.each(cls, function(c){
17404             if(c == 'bottom'){
17405                 _this.picker().setTop(_this.inputEl().getHeight());
17406                 return;
17407             }
17408             if(c == 'top'){
17409                 _this.picker().setTop(0 - _this.picker().getHeight());
17410                 return;
17411             }
17412             
17413             if(c == 'left'){
17414                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17415                 return;
17416             }
17417             if(c == 'right'){
17418                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17419                 return;
17420             }
17421         });
17422         
17423     },
17424   
17425     onFocus : function()
17426     {
17427         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17428         this.show();
17429     },
17430     
17431     onBlur : function()
17432     {
17433         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17434         this.hide();
17435     },
17436     
17437     show : function()
17438     {
17439         this.picker().show();
17440         this.pop.show();
17441         this.update();
17442         this.place();
17443         
17444         this.fireEvent('show', this, this.date);
17445     },
17446     
17447     hide : function()
17448     {
17449         this.picker().hide();
17450         this.pop.hide();
17451         
17452         this.fireEvent('hide', this, this.date);
17453     },
17454     
17455     setTime : function()
17456     {
17457         this.hide();
17458         this.setValue(this.time.format(this.format));
17459         
17460         this.fireEvent('select', this, this.date);
17461         
17462         
17463     },
17464     
17465     onMousedown: function(e){
17466         e.stopPropagation();
17467         e.preventDefault();
17468     },
17469     
17470     onIncrementHours: function()
17471     {
17472         Roo.log('onIncrementHours');
17473         this.time = this.time.add(Date.HOUR, 1);
17474         this.update();
17475         
17476     },
17477     
17478     onDecrementHours: function()
17479     {
17480         Roo.log('onDecrementHours');
17481         this.time = this.time.add(Date.HOUR, -1);
17482         this.update();
17483     },
17484     
17485     onIncrementMinutes: function()
17486     {
17487         Roo.log('onIncrementMinutes');
17488         this.time = this.time.add(Date.MINUTE, 1);
17489         this.update();
17490     },
17491     
17492     onDecrementMinutes: function()
17493     {
17494         Roo.log('onDecrementMinutes');
17495         this.time = this.time.add(Date.MINUTE, -1);
17496         this.update();
17497     },
17498     
17499     onTogglePeriod: function()
17500     {
17501         Roo.log('onTogglePeriod');
17502         this.time = this.time.add(Date.HOUR, 12);
17503         this.update();
17504     }
17505     
17506    
17507 });
17508
17509 Roo.apply(Roo.bootstrap.TimeField,  {
17510     
17511     content : {
17512         tag: 'tbody',
17513         cn: [
17514             {
17515                 tag: 'tr',
17516                 cn: [
17517                 {
17518                     tag: 'td',
17519                     colspan: '7'
17520                 }
17521                 ]
17522             }
17523         ]
17524     },
17525     
17526     footer : {
17527         tag: 'tfoot',
17528         cn: [
17529             {
17530                 tag: 'tr',
17531                 cn: [
17532                 {
17533                     tag: 'th',
17534                     colspan: '7',
17535                     cls: '',
17536                     cn: [
17537                         {
17538                             tag: 'button',
17539                             cls: 'btn btn-info ok',
17540                             html: 'OK'
17541                         }
17542                     ]
17543                 }
17544
17545                 ]
17546             }
17547         ]
17548     }
17549 });
17550
17551 Roo.apply(Roo.bootstrap.TimeField,  {
17552   
17553     template : {
17554         tag: 'div',
17555         cls: 'datepicker dropdown-menu',
17556         cn: [
17557             {
17558                 tag: 'div',
17559                 cls: 'datepicker-time',
17560                 cn: [
17561                 {
17562                     tag: 'table',
17563                     cls: 'table-condensed',
17564                     cn:[
17565                     Roo.bootstrap.TimeField.content,
17566                     Roo.bootstrap.TimeField.footer
17567                     ]
17568                 }
17569                 ]
17570             }
17571         ]
17572     }
17573 });
17574
17575  
17576
17577  /*
17578  * - LGPL
17579  *
17580  * MonthField
17581  * 
17582  */
17583
17584 /**
17585  * @class Roo.bootstrap.MonthField
17586  * @extends Roo.bootstrap.Input
17587  * Bootstrap MonthField class
17588  * 
17589  * @cfg {String} language default en
17590  * 
17591  * @constructor
17592  * Create a new MonthField
17593  * @param {Object} config The config object
17594  */
17595
17596 Roo.bootstrap.MonthField = function(config){
17597     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17598     
17599     this.addEvents({
17600         /**
17601          * @event show
17602          * Fires when this field show.
17603          * @param {Roo.bootstrap.MonthField} this
17604          * @param {Mixed} date The date value
17605          */
17606         show : true,
17607         /**
17608          * @event show
17609          * Fires when this field hide.
17610          * @param {Roo.bootstrap.MonthField} this
17611          * @param {Mixed} date The date value
17612          */
17613         hide : true,
17614         /**
17615          * @event select
17616          * Fires when select a date.
17617          * @param {Roo.bootstrap.MonthField} this
17618          * @param {String} oldvalue The old value
17619          * @param {String} newvalue The new value
17620          */
17621         select : true
17622     });
17623 };
17624
17625 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17626     
17627     onRender: function(ct, position)
17628     {
17629         
17630         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17631         
17632         this.language = this.language || 'en';
17633         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17634         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17635         
17636         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17637         this.isInline = false;
17638         this.isInput = true;
17639         this.component = this.el.select('.add-on', true).first() || false;
17640         this.component = (this.component && this.component.length === 0) ? false : this.component;
17641         this.hasInput = this.component && this.inputEL().length;
17642         
17643         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17644         
17645         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17646         
17647         this.picker().on('mousedown', this.onMousedown, this);
17648         this.picker().on('click', this.onClick, this);
17649         
17650         this.picker().addClass('datepicker-dropdown');
17651         
17652         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17653             v.setStyle('width', '189px');
17654         });
17655         
17656         this.fillMonths();
17657         
17658         this.update();
17659         
17660         if(this.isInline) {
17661             this.show();
17662         }
17663         
17664     },
17665     
17666     setValue: function(v, suppressEvent)
17667     {   
17668         var o = this.getValue();
17669         
17670         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17671         
17672         this.update();
17673
17674         if(suppressEvent !== true){
17675             this.fireEvent('select', this, o, v);
17676         }
17677         
17678     },
17679     
17680     getValue: function()
17681     {
17682         return this.value;
17683     },
17684     
17685     onClick: function(e) 
17686     {
17687         e.stopPropagation();
17688         e.preventDefault();
17689         
17690         var target = e.getTarget();
17691         
17692         if(target.nodeName.toLowerCase() === 'i'){
17693             target = Roo.get(target).dom.parentNode;
17694         }
17695         
17696         var nodeName = target.nodeName;
17697         var className = target.className;
17698         var html = target.innerHTML;
17699         
17700         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17701             return;
17702         }
17703         
17704         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17705         
17706         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17707         
17708         this.hide();
17709                         
17710     },
17711     
17712     picker : function()
17713     {
17714         return this.pickerEl;
17715     },
17716     
17717     fillMonths: function()
17718     {    
17719         var i = 0;
17720         var months = this.picker().select('>.datepicker-months td', true).first();
17721         
17722         months.dom.innerHTML = '';
17723         
17724         while (i < 12) {
17725             var month = {
17726                 tag: 'span',
17727                 cls: 'month',
17728                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17729             }
17730             
17731             months.createChild(month);
17732         }
17733         
17734     },
17735     
17736     update: function()
17737     {
17738         var _this = this;
17739         
17740         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17741             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17742         }
17743         
17744         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17745             e.removeClass('active');
17746             
17747             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17748                 e.addClass('active');
17749             }
17750         })
17751     },
17752     
17753     place: function()
17754     {
17755         if(this.isInline) return;
17756         
17757         this.picker().removeClass(['bottom', 'top']);
17758         
17759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17760             /*
17761              * place to the top of element!
17762              *
17763              */
17764             
17765             this.picker().addClass('top');
17766             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17767             
17768             return;
17769         }
17770         
17771         this.picker().addClass('bottom');
17772         
17773         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17774     },
17775     
17776     onFocus : function()
17777     {
17778         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17779         this.show();
17780     },
17781     
17782     onBlur : function()
17783     {
17784         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17785         
17786         var d = this.inputEl().getValue();
17787         
17788         this.setValue(d);
17789                 
17790         this.hide();
17791     },
17792     
17793     show : function()
17794     {
17795         this.picker().show();
17796         this.picker().select('>.datepicker-months', true).first().show();
17797         this.update();
17798         this.place();
17799         
17800         this.fireEvent('show', this, this.date);
17801     },
17802     
17803     hide : function()
17804     {
17805         if(this.isInline) return;
17806         this.picker().hide();
17807         this.fireEvent('hide', this, this.date);
17808         
17809     },
17810     
17811     onMousedown: function(e)
17812     {
17813         e.stopPropagation();
17814         e.preventDefault();
17815     },
17816     
17817     keyup: function(e)
17818     {
17819         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17820         this.update();
17821     },
17822
17823     fireKey: function(e)
17824     {
17825         if (!this.picker().isVisible()){
17826             if (e.keyCode == 27) // allow escape to hide and re-show picker
17827                 this.show();
17828             return;
17829         }
17830         
17831         var dir;
17832         
17833         switch(e.keyCode){
17834             case 27: // escape
17835                 this.hide();
17836                 e.preventDefault();
17837                 break;
17838             case 37: // left
17839             case 39: // right
17840                 dir = e.keyCode == 37 ? -1 : 1;
17841                 
17842                 this.vIndex = this.vIndex + dir;
17843                 
17844                 if(this.vIndex < 0){
17845                     this.vIndex = 0;
17846                 }
17847                 
17848                 if(this.vIndex > 11){
17849                     this.vIndex = 11;
17850                 }
17851                 
17852                 if(isNaN(this.vIndex)){
17853                     this.vIndex = 0;
17854                 }
17855                 
17856                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17857                 
17858                 break;
17859             case 38: // up
17860             case 40: // down
17861                 
17862                 dir = e.keyCode == 38 ? -1 : 1;
17863                 
17864                 this.vIndex = this.vIndex + dir * 4;
17865                 
17866                 if(this.vIndex < 0){
17867                     this.vIndex = 0;
17868                 }
17869                 
17870                 if(this.vIndex > 11){
17871                     this.vIndex = 11;
17872                 }
17873                 
17874                 if(isNaN(this.vIndex)){
17875                     this.vIndex = 0;
17876                 }
17877                 
17878                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17879                 break;
17880                 
17881             case 13: // enter
17882                 
17883                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17884                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17885                 }
17886                 
17887                 this.hide();
17888                 e.preventDefault();
17889                 break;
17890             case 9: // tab
17891                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17892                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17893                 }
17894                 this.hide();
17895                 break;
17896             case 16: // shift
17897             case 17: // ctrl
17898             case 18: // alt
17899                 break;
17900             default :
17901                 this.hide();
17902                 
17903         }
17904     },
17905     
17906     remove: function() 
17907     {
17908         this.picker().remove();
17909     }
17910    
17911 });
17912
17913 Roo.apply(Roo.bootstrap.MonthField,  {
17914     
17915     content : {
17916         tag: 'tbody',
17917         cn: [
17918         {
17919             tag: 'tr',
17920             cn: [
17921             {
17922                 tag: 'td',
17923                 colspan: '7'
17924             }
17925             ]
17926         }
17927         ]
17928     },
17929     
17930     dates:{
17931         en: {
17932             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17933             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17934         }
17935     }
17936 });
17937
17938 Roo.apply(Roo.bootstrap.MonthField,  {
17939   
17940     template : {
17941         tag: 'div',
17942         cls: 'datepicker dropdown-menu roo-dynamic',
17943         cn: [
17944             {
17945                 tag: 'div',
17946                 cls: 'datepicker-months',
17947                 cn: [
17948                 {
17949                     tag: 'table',
17950                     cls: 'table-condensed',
17951                     cn:[
17952                         Roo.bootstrap.DateField.content
17953                     ]
17954                 }
17955                 ]
17956             }
17957         ]
17958     }
17959 });
17960
17961  
17962
17963  
17964  /*
17965  * - LGPL
17966  *
17967  * CheckBox
17968  * 
17969  */
17970
17971 /**
17972  * @class Roo.bootstrap.CheckBox
17973  * @extends Roo.bootstrap.Input
17974  * Bootstrap CheckBox class
17975  * 
17976  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17977  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17978  * @cfg {String} boxLabel The text that appears beside the checkbox
17979  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17980  * @cfg {Boolean} checked initnal the element
17981  * @cfg {Boolean} inline inline the element (default false)
17982  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17983  * 
17984  * @constructor
17985  * Create a new CheckBox
17986  * @param {Object} config The config object
17987  */
17988
17989 Roo.bootstrap.CheckBox = function(config){
17990     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17991    
17992     this.addEvents({
17993         /**
17994         * @event check
17995         * Fires when the element is checked or unchecked.
17996         * @param {Roo.bootstrap.CheckBox} this This input
17997         * @param {Boolean} checked The new checked value
17998         */
17999        check : true
18000     });
18001     
18002 };
18003
18004 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18005   
18006     inputType: 'checkbox',
18007     inputValue: 1,
18008     valueOff: 0,
18009     boxLabel: false,
18010     checked: false,
18011     weight : false,
18012     inline: false,
18013     
18014     getAutoCreate : function()
18015     {
18016         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18017         
18018         var id = Roo.id();
18019         
18020         var cfg = {};
18021         
18022         cfg.cls = 'form-group ' + this.inputType; //input-group
18023         
18024         if(this.inline){
18025             cfg.cls += ' ' + this.inputType + '-inline';
18026         }
18027         
18028         var input =  {
18029             tag: 'input',
18030             id : id,
18031             type : this.inputType,
18032             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18033             cls : 'roo-' + this.inputType, //'form-box',
18034             placeholder : this.placeholder || ''
18035             
18036         };
18037         
18038         if (this.weight) { // Validity check?
18039             cfg.cls += " " + this.inputType + "-" + this.weight;
18040         }
18041         
18042         if (this.disabled) {
18043             input.disabled=true;
18044         }
18045         
18046         if(this.checked){
18047             input.checked = this.checked;
18048         }
18049         
18050         if (this.name) {
18051             input.name = this.name;
18052         }
18053         
18054         if (this.size) {
18055             input.cls += ' input-' + this.size;
18056         }
18057         
18058         var settings=this;
18059         
18060         ['xs','sm','md','lg'].map(function(size){
18061             if (settings[size]) {
18062                 cfg.cls += ' col-' + size + '-' + settings[size];
18063             }
18064         });
18065         
18066         var inputblock = input;
18067          
18068         if (this.before || this.after) {
18069             
18070             inputblock = {
18071                 cls : 'input-group',
18072                 cn :  [] 
18073             };
18074             
18075             if (this.before) {
18076                 inputblock.cn.push({
18077                     tag :'span',
18078                     cls : 'input-group-addon',
18079                     html : this.before
18080                 });
18081             }
18082             
18083             inputblock.cn.push(input);
18084             
18085             if (this.after) {
18086                 inputblock.cn.push({
18087                     tag :'span',
18088                     cls : 'input-group-addon',
18089                     html : this.after
18090                 });
18091             }
18092             
18093         }
18094         
18095         if (align ==='left' && this.fieldLabel.length) {
18096                 Roo.log("left and has label");
18097                 cfg.cn = [
18098                     
18099                     {
18100                         tag: 'label',
18101                         'for' :  id,
18102                         cls : 'control-label col-md-' + this.labelWidth,
18103                         html : this.fieldLabel
18104                         
18105                     },
18106                     {
18107                         cls : "col-md-" + (12 - this.labelWidth), 
18108                         cn: [
18109                             inputblock
18110                         ]
18111                     }
18112                     
18113                 ];
18114         } else if ( this.fieldLabel.length) {
18115                 Roo.log(" label");
18116                 cfg.cn = [
18117                    
18118                     {
18119                         tag: this.boxLabel ? 'span' : 'label',
18120                         'for': id,
18121                         cls: 'control-label box-input-label',
18122                         //cls : 'input-group-addon',
18123                         html : this.fieldLabel
18124                         
18125                     },
18126                     
18127                     inputblock
18128                     
18129                 ];
18130
18131         } else {
18132             
18133                 Roo.log(" no label && no align");
18134                 cfg.cn = [  inputblock ] ;
18135                 
18136                 
18137         }
18138         if(this.boxLabel){
18139              var boxLabelCfg = {
18140                 tag: 'label',
18141                 //'for': id, // box label is handled by onclick - so no for...
18142                 cls: 'box-label',
18143                 html: this.boxLabel
18144             }
18145             
18146             if(this.tooltip){
18147                 boxLabelCfg.tooltip = this.tooltip;
18148             }
18149              
18150             cfg.cn.push(boxLabelCfg);
18151         }
18152         
18153         
18154        
18155         return cfg;
18156         
18157     },
18158     
18159     /**
18160      * return the real input element.
18161      */
18162     inputEl: function ()
18163     {
18164         return this.el.select('input.roo-' + this.inputType,true).first();
18165     },
18166     
18167     labelEl: function()
18168     {
18169         return this.el.select('label.control-label',true).first();
18170     },
18171     /* depricated... */
18172     
18173     label: function()
18174     {
18175         return this.labelEl();
18176     },
18177     
18178     initEvents : function()
18179     {
18180 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18181         
18182         this.inputEl().on('click', this.onClick,  this);
18183         
18184         if (this.boxLabel) { 
18185             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18186         }
18187         
18188         this.startValue = this.getValue();
18189         
18190         if(this.groupId){
18191             Roo.bootstrap.CheckBox.register(this);
18192         }
18193     },
18194     
18195     onClick : function()
18196     {   
18197         this.setChecked(!this.checked);
18198     },
18199     
18200     setChecked : function(state,suppressEvent)
18201     {
18202         this.startValue = this.getValue();
18203         
18204         if(this.inputType == 'radio'){
18205             
18206             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18207                 e.dom.checked = false;
18208             });
18209             
18210             this.inputEl().dom.checked = true;
18211             
18212             this.inputEl().dom.value = this.inputValue;
18213             
18214             if(suppressEvent !== true){
18215                 this.fireEvent('check', this, true);
18216             }
18217             
18218             this.validate();
18219             
18220             return;
18221         }
18222         
18223         this.checked = state;
18224         
18225         this.inputEl().dom.checked = state;
18226         
18227         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18228         
18229         if(suppressEvent !== true){
18230             this.fireEvent('check', this, state);
18231         }
18232         
18233         this.validate();
18234     },
18235     
18236     getValue : function()
18237     {
18238         if(this.inputType == 'radio'){
18239             return this.getGroupValue();
18240         }
18241         
18242         return this.inputEl().getValue();
18243         
18244     },
18245     
18246     getGroupValue : function()
18247     {
18248         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18249             return '';
18250         }
18251         
18252         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18253     },
18254     
18255     setValue : function(v,suppressEvent)
18256     {
18257         if(this.inputType == 'radio'){
18258             this.setGroupValue(v, suppressEvent);
18259             return;
18260         }
18261         
18262         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18263         
18264         this.validate();
18265     },
18266     
18267     setGroupValue : function(v, suppressEvent)
18268     {
18269         this.startValue = this.getValue();
18270         
18271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18272             e.dom.checked = false;
18273             
18274             if(e.dom.value == v){
18275                 e.dom.checked = true;
18276             }
18277         });
18278         
18279         if(suppressEvent !== true){
18280             this.fireEvent('check', this, true);
18281         }
18282
18283         this.validate();
18284         
18285         return;
18286     },
18287     
18288     validate : function()
18289     {
18290         if(
18291                 this.disabled || 
18292                 (this.inputType == 'radio' && this.validateRadio()) ||
18293                 (this.inputType == 'checkbox' && this.validateCheckbox())
18294         ){
18295             this.markValid();
18296             return true;
18297         }
18298         
18299         this.markInvalid();
18300         return false;
18301     },
18302     
18303     validateRadio : function()
18304     {
18305         var valid = false;
18306         
18307         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18308             if(!e.dom.checked){
18309                 return;
18310             }
18311             
18312             valid = true;
18313             
18314             return false;
18315         });
18316         
18317         return valid;
18318     },
18319     
18320     validateCheckbox : function()
18321     {
18322         if(!this.groupId){
18323             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18324         }
18325         
18326         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18327         
18328         if(!group){
18329             return false;
18330         }
18331         
18332         var r = false;
18333         
18334         for(var i in group){
18335             if(r){
18336                 break;
18337             }
18338             
18339             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18340         }
18341         
18342         return r;
18343     },
18344     
18345     /**
18346      * Mark this field as valid
18347      */
18348     markValid : function()
18349     {
18350         if(this.allowBlank){
18351             return;
18352         }
18353         
18354         var _this = this;
18355         
18356         this.fireEvent('valid', this);
18357         
18358         if(this.inputType == 'radio'){
18359             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18360                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18361                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18362             });
18363             
18364             return;
18365         }
18366         
18367         if(!this.groupId){
18368             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18369             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18370             return;
18371         }
18372         
18373         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18374             
18375         if(!group){
18376             return;
18377         }
18378         
18379         for(var i in group){
18380             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18381             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18382         }
18383     },
18384     
18385      /**
18386      * Mark this field as invalid
18387      * @param {String} msg The validation message
18388      */
18389     markInvalid : function(msg)
18390     {
18391         if(this.allowBlank){
18392             return;
18393         }
18394         
18395         var _this = this;
18396         
18397         this.fireEvent('invalid', this, msg);
18398         
18399         if(this.inputType == 'radio'){
18400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18401                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18402                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18403             });
18404             
18405             return;
18406         }
18407         
18408         if(!this.groupId){
18409             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18410             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18411             return;
18412         }
18413         
18414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18415             
18416         if(!group){
18417             return;
18418         }
18419         
18420         for(var i in group){
18421             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18422             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18423         }
18424         
18425     }
18426     
18427 });
18428
18429 Roo.apply(Roo.bootstrap.CheckBox, {
18430     
18431     groups: {},
18432     
18433      /**
18434     * register a CheckBox Group
18435     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18436     */
18437     register : function(checkbox)
18438     {
18439         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18440             this.groups[checkbox.groupId] = {};
18441         }
18442         
18443         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18444             return;
18445         }
18446         
18447         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18448         
18449     },
18450     /**
18451     * fetch a CheckBox Group based on the group ID
18452     * @param {string} the group ID
18453     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18454     */
18455     get: function(groupId) {
18456         if (typeof(this.groups[groupId]) == 'undefined') {
18457             return false;
18458         }
18459         
18460         return this.groups[groupId] ;
18461     }
18462     
18463     
18464 });
18465 /*
18466  * - LGPL
18467  *
18468  * Radio
18469  *
18470  *
18471  * not inline
18472  *<div class="radio">
18473   <label>
18474     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18475     Option one is this and that&mdash;be sure to include why it's great
18476   </label>
18477 </div>
18478  *
18479  *
18480  *inline
18481  *<span>
18482  *<label class="radio-inline">fieldLabel</label>
18483  *<label class="radio-inline">
18484   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18485 </label>
18486 <span>
18487  * 
18488  * 
18489  */
18490
18491 /**
18492  * @class Roo.bootstrap.Radio
18493  * @extends Roo.bootstrap.CheckBox
18494  * Bootstrap Radio class
18495
18496  * @constructor
18497  * Create a new Radio
18498  * @param {Object} config The config object
18499  */
18500
18501 Roo.bootstrap.Radio = function(config){
18502     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18503    
18504 };
18505
18506 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18507     
18508     inputType: 'radio',
18509     inputValue: '',
18510     valueOff: '',
18511     
18512     getAutoCreate : function()
18513     {
18514         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18515         align = align || 'left'; // default...
18516         
18517         
18518         
18519         var id = Roo.id();
18520         
18521         var cfg = {
18522                 tag : this.inline ? 'span' : 'div',
18523                 cls : '',
18524                 cn : []
18525         };
18526         
18527         var inline = this.inline ? ' radio-inline' : '';
18528         
18529         var lbl = {
18530                 tag: 'label' ,
18531                 // does not need for, as we wrap the input with it..
18532                 'for' : id,
18533                 cls : 'control-label box-label' + inline,
18534                 cn : []
18535         };
18536         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18537         
18538         var fieldLabel = {
18539             tag: 'label' ,
18540             //cls : 'control-label' + inline,
18541             html : this.fieldLabel,
18542             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18543         };
18544         
18545  
18546         
18547         
18548         var input =  {
18549             tag: 'input',
18550             id : id,
18551             type : this.inputType,
18552             //value : (!this.checked) ? this.valueOff : this.inputValue,
18553             value : this.inputValue,
18554             cls : 'roo-radio',
18555             placeholder : this.placeholder || '' // ?? needed????
18556             
18557         };
18558         if (this.weight) { // Validity check?
18559             input.cls += " radio-" + this.weight;
18560         }
18561         if (this.disabled) {
18562             input.disabled=true;
18563         }
18564         
18565         if(this.checked){
18566             input.checked = this.checked;
18567         }
18568         
18569         if (this.name) {
18570             input.name = this.name;
18571         }
18572         
18573         if (this.size) {
18574             input.cls += ' input-' + this.size;
18575         }
18576         
18577         //?? can span's inline have a width??
18578         
18579         var settings=this;
18580         ['xs','sm','md','lg'].map(function(size){
18581             if (settings[size]) {
18582                 cfg.cls += ' col-' + size + '-' + settings[size];
18583             }
18584         });
18585         
18586         var inputblock = input;
18587         
18588         if (this.before || this.after) {
18589             
18590             inputblock = {
18591                 cls : 'input-group',
18592                 tag : 'span',
18593                 cn :  [] 
18594             };
18595             if (this.before) {
18596                 inputblock.cn.push({
18597                     tag :'span',
18598                     cls : 'input-group-addon',
18599                     html : this.before
18600                 });
18601             }
18602             inputblock.cn.push(input);
18603             if (this.after) {
18604                 inputblock.cn.push({
18605                     tag :'span',
18606                     cls : 'input-group-addon',
18607                     html : this.after
18608                 });
18609             }
18610             
18611         };
18612         
18613         
18614         if (this.fieldLabel && this.fieldLabel.length) {
18615             cfg.cn.push(fieldLabel);
18616         }
18617        
18618         // normal bootstrap puts the input inside the label.
18619         // however with our styled version - it has to go after the input.
18620        
18621         //lbl.cn.push(inputblock);
18622         
18623         var lblwrap =  {
18624             tag: 'span',
18625             cls: 'radio' + inline,
18626             cn: [
18627                 inputblock,
18628                 lbl
18629             ]
18630         };
18631         
18632         cfg.cn.push( lblwrap);
18633         
18634         if(this.boxLabel){
18635             lbl.cn.push({
18636                 tag: 'span',
18637                 html: this.boxLabel
18638             })
18639         }
18640          
18641         
18642         return cfg;
18643         
18644     },
18645     
18646     initEvents : function()
18647     {
18648 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18649         
18650         this.inputEl().on('click', this.onClick,  this);
18651         if (this.boxLabel) {
18652             Roo.log('find label')
18653             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18654         }
18655         
18656     },
18657     
18658     inputEl: function ()
18659     {
18660         return this.el.select('input.roo-radio',true).first();
18661     },
18662     onClick : function()
18663     {   
18664         Roo.log("click");
18665         this.setChecked(true);
18666     },
18667     
18668     setChecked : function(state,suppressEvent)
18669     {
18670         if(state){
18671             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18672                 v.dom.checked = false;
18673             });
18674         }
18675         Roo.log(this.inputEl().dom);
18676         this.checked = state;
18677         this.inputEl().dom.checked = state;
18678         
18679         if(suppressEvent !== true){
18680             this.fireEvent('check', this, state);
18681         }
18682         
18683         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18684         
18685     },
18686     
18687     getGroupValue : function()
18688     {
18689         var value = '';
18690         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18691             if(v.dom.checked == true){
18692                 value = v.dom.value;
18693             }
18694         });
18695         
18696         return value;
18697     },
18698     
18699     /**
18700      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18701      * @return {Mixed} value The field value
18702      */
18703     getValue : function(){
18704         return this.getGroupValue();
18705     }
18706     
18707 });
18708
18709  
18710 //<script type="text/javascript">
18711
18712 /*
18713  * Based  Ext JS Library 1.1.1
18714  * Copyright(c) 2006-2007, Ext JS, LLC.
18715  * LGPL
18716  *
18717  */
18718  
18719 /**
18720  * @class Roo.HtmlEditorCore
18721  * @extends Roo.Component
18722  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18723  *
18724  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18725  */
18726
18727 Roo.HtmlEditorCore = function(config){
18728     
18729     
18730     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18731     
18732     
18733     this.addEvents({
18734         /**
18735          * @event initialize
18736          * Fires when the editor is fully initialized (including the iframe)
18737          * @param {Roo.HtmlEditorCore} this
18738          */
18739         initialize: true,
18740         /**
18741          * @event activate
18742          * Fires when the editor is first receives the focus. Any insertion must wait
18743          * until after this event.
18744          * @param {Roo.HtmlEditorCore} this
18745          */
18746         activate: true,
18747          /**
18748          * @event beforesync
18749          * Fires before the textarea is updated with content from the editor iframe. Return false
18750          * to cancel the sync.
18751          * @param {Roo.HtmlEditorCore} this
18752          * @param {String} html
18753          */
18754         beforesync: true,
18755          /**
18756          * @event beforepush
18757          * Fires before the iframe editor is updated with content from the textarea. Return false
18758          * to cancel the push.
18759          * @param {Roo.HtmlEditorCore} this
18760          * @param {String} html
18761          */
18762         beforepush: true,
18763          /**
18764          * @event sync
18765          * Fires when the textarea is updated with content from the editor iframe.
18766          * @param {Roo.HtmlEditorCore} this
18767          * @param {String} html
18768          */
18769         sync: true,
18770          /**
18771          * @event push
18772          * Fires when the iframe editor is updated with content from the textarea.
18773          * @param {Roo.HtmlEditorCore} this
18774          * @param {String} html
18775          */
18776         push: true,
18777         
18778         /**
18779          * @event editorevent
18780          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18781          * @param {Roo.HtmlEditorCore} this
18782          */
18783         editorevent: true
18784         
18785     });
18786     
18787     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18788     
18789     // defaults : white / black...
18790     this.applyBlacklists();
18791     
18792     
18793     
18794 };
18795
18796
18797 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18798
18799
18800      /**
18801      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18802      */
18803     
18804     owner : false,
18805     
18806      /**
18807      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18808      *                        Roo.resizable.
18809      */
18810     resizable : false,
18811      /**
18812      * @cfg {Number} height (in pixels)
18813      */   
18814     height: 300,
18815    /**
18816      * @cfg {Number} width (in pixels)
18817      */   
18818     width: 500,
18819     
18820     /**
18821      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18822      * 
18823      */
18824     stylesheets: false,
18825     
18826     // id of frame..
18827     frameId: false,
18828     
18829     // private properties
18830     validationEvent : false,
18831     deferHeight: true,
18832     initialized : false,
18833     activated : false,
18834     sourceEditMode : false,
18835     onFocus : Roo.emptyFn,
18836     iframePad:3,
18837     hideMode:'offsets',
18838     
18839     clearUp: true,
18840     
18841     // blacklist + whitelisted elements..
18842     black: false,
18843     white: false,
18844      
18845     
18846
18847     /**
18848      * Protected method that will not generally be called directly. It
18849      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18850      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18851      */
18852     getDocMarkup : function(){
18853         // body styles..
18854         var st = '';
18855         
18856         // inherit styels from page...?? 
18857         if (this.stylesheets === false) {
18858             
18859             Roo.get(document.head).select('style').each(function(node) {
18860                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18861             });
18862             
18863             Roo.get(document.head).select('link').each(function(node) { 
18864                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18865             });
18866             
18867         } else if (!this.stylesheets.length) {
18868                 // simple..
18869                 st = '<style type="text/css">' +
18870                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18871                    '</style>';
18872         } else { 
18873             
18874         }
18875         
18876         st +=  '<style type="text/css">' +
18877             'IMG { cursor: pointer } ' +
18878         '</style>';
18879
18880         
18881         return '<html><head>' + st  +
18882             //<style type="text/css">' +
18883             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18884             //'</style>' +
18885             ' </head><body class="roo-htmleditor-body"></body></html>';
18886     },
18887
18888     // private
18889     onRender : function(ct, position)
18890     {
18891         var _t = this;
18892         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18893         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18894         
18895         
18896         this.el.dom.style.border = '0 none';
18897         this.el.dom.setAttribute('tabIndex', -1);
18898         this.el.addClass('x-hidden hide');
18899         
18900         
18901         
18902         if(Roo.isIE){ // fix IE 1px bogus margin
18903             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18904         }
18905        
18906         
18907         this.frameId = Roo.id();
18908         
18909          
18910         
18911         var iframe = this.owner.wrap.createChild({
18912             tag: 'iframe',
18913             cls: 'form-control', // bootstrap..
18914             id: this.frameId,
18915             name: this.frameId,
18916             frameBorder : 'no',
18917             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18918         }, this.el
18919         );
18920         
18921         
18922         this.iframe = iframe.dom;
18923
18924          this.assignDocWin();
18925         
18926         this.doc.designMode = 'on';
18927        
18928         this.doc.open();
18929         this.doc.write(this.getDocMarkup());
18930         this.doc.close();
18931
18932         
18933         var task = { // must defer to wait for browser to be ready
18934             run : function(){
18935                 //console.log("run task?" + this.doc.readyState);
18936                 this.assignDocWin();
18937                 if(this.doc.body || this.doc.readyState == 'complete'){
18938                     try {
18939                         this.doc.designMode="on";
18940                     } catch (e) {
18941                         return;
18942                     }
18943                     Roo.TaskMgr.stop(task);
18944                     this.initEditor.defer(10, this);
18945                 }
18946             },
18947             interval : 10,
18948             duration: 10000,
18949             scope: this
18950         };
18951         Roo.TaskMgr.start(task);
18952
18953     },
18954
18955     // private
18956     onResize : function(w, h)
18957     {
18958          Roo.log('resize: ' +w + ',' + h );
18959         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18960         if(!this.iframe){
18961             return;
18962         }
18963         if(typeof w == 'number'){
18964             
18965             this.iframe.style.width = w + 'px';
18966         }
18967         if(typeof h == 'number'){
18968             
18969             this.iframe.style.height = h + 'px';
18970             if(this.doc){
18971                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18972             }
18973         }
18974         
18975     },
18976
18977     /**
18978      * Toggles the editor between standard and source edit mode.
18979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18980      */
18981     toggleSourceEdit : function(sourceEditMode){
18982         
18983         this.sourceEditMode = sourceEditMode === true;
18984         
18985         if(this.sourceEditMode){
18986  
18987             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18988             
18989         }else{
18990             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18991             //this.iframe.className = '';
18992             this.deferFocus();
18993         }
18994         //this.setSize(this.owner.wrap.getSize());
18995         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18996     },
18997
18998     
18999   
19000
19001     /**
19002      * Protected method that will not generally be called directly. If you need/want
19003      * custom HTML cleanup, this is the method you should override.
19004      * @param {String} html The HTML to be cleaned
19005      * return {String} The cleaned HTML
19006      */
19007     cleanHtml : function(html){
19008         html = String(html);
19009         if(html.length > 5){
19010             if(Roo.isSafari){ // strip safari nonsense
19011                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19012             }
19013         }
19014         if(html == '&nbsp;'){
19015             html = '';
19016         }
19017         return html;
19018     },
19019
19020     /**
19021      * HTML Editor -> Textarea
19022      * Protected method that will not generally be called directly. Syncs the contents
19023      * of the editor iframe with the textarea.
19024      */
19025     syncValue : function(){
19026         if(this.initialized){
19027             var bd = (this.doc.body || this.doc.documentElement);
19028             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19029             var html = bd.innerHTML;
19030             if(Roo.isSafari){
19031                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19032                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19033                 if(m && m[1]){
19034                     html = '<div style="'+m[0]+'">' + html + '</div>';
19035                 }
19036             }
19037             html = this.cleanHtml(html);
19038             // fix up the special chars.. normaly like back quotes in word...
19039             // however we do not want to do this with chinese..
19040             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19041                 var cc = b.charCodeAt();
19042                 if (
19043                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19044                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19045                     (cc >= 0xf900 && cc < 0xfb00 )
19046                 ) {
19047                         return b;
19048                 }
19049                 return "&#"+cc+";" 
19050             });
19051             if(this.owner.fireEvent('beforesync', this, html) !== false){
19052                 this.el.dom.value = html;
19053                 this.owner.fireEvent('sync', this, html);
19054             }
19055         }
19056     },
19057
19058     /**
19059      * Protected method that will not generally be called directly. Pushes the value of the textarea
19060      * into the iframe editor.
19061      */
19062     pushValue : function(){
19063         if(this.initialized){
19064             var v = this.el.dom.value.trim();
19065             
19066 //            if(v.length < 1){
19067 //                v = '&#160;';
19068 //            }
19069             
19070             if(this.owner.fireEvent('beforepush', this, v) !== false){
19071                 var d = (this.doc.body || this.doc.documentElement);
19072                 d.innerHTML = v;
19073                 this.cleanUpPaste();
19074                 this.el.dom.value = d.innerHTML;
19075                 this.owner.fireEvent('push', this, v);
19076             }
19077         }
19078     },
19079
19080     // private
19081     deferFocus : function(){
19082         this.focus.defer(10, this);
19083     },
19084
19085     // doc'ed in Field
19086     focus : function(){
19087         if(this.win && !this.sourceEditMode){
19088             this.win.focus();
19089         }else{
19090             this.el.focus();
19091         }
19092     },
19093     
19094     assignDocWin: function()
19095     {
19096         var iframe = this.iframe;
19097         
19098          if(Roo.isIE){
19099             this.doc = iframe.contentWindow.document;
19100             this.win = iframe.contentWindow;
19101         } else {
19102 //            if (!Roo.get(this.frameId)) {
19103 //                return;
19104 //            }
19105 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19106 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19107             
19108             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19109                 return;
19110             }
19111             
19112             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19113             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19114         }
19115     },
19116     
19117     // private
19118     initEditor : function(){
19119         //console.log("INIT EDITOR");
19120         this.assignDocWin();
19121         
19122         
19123         
19124         this.doc.designMode="on";
19125         this.doc.open();
19126         this.doc.write(this.getDocMarkup());
19127         this.doc.close();
19128         
19129         var dbody = (this.doc.body || this.doc.documentElement);
19130         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19131         // this copies styles from the containing element into thsi one..
19132         // not sure why we need all of this..
19133         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19134         
19135         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19136         //ss['background-attachment'] = 'fixed'; // w3c
19137         dbody.bgProperties = 'fixed'; // ie
19138         //Roo.DomHelper.applyStyles(dbody, ss);
19139         Roo.EventManager.on(this.doc, {
19140             //'mousedown': this.onEditorEvent,
19141             'mouseup': this.onEditorEvent,
19142             'dblclick': this.onEditorEvent,
19143             'click': this.onEditorEvent,
19144             'keyup': this.onEditorEvent,
19145             buffer:100,
19146             scope: this
19147         });
19148         if(Roo.isGecko){
19149             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19150         }
19151         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19152             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19153         }
19154         this.initialized = true;
19155
19156         this.owner.fireEvent('initialize', this);
19157         this.pushValue();
19158     },
19159
19160     // private
19161     onDestroy : function(){
19162         
19163         
19164         
19165         if(this.rendered){
19166             
19167             //for (var i =0; i < this.toolbars.length;i++) {
19168             //    // fixme - ask toolbars for heights?
19169             //    this.toolbars[i].onDestroy();
19170            // }
19171             
19172             //this.wrap.dom.innerHTML = '';
19173             //this.wrap.remove();
19174         }
19175     },
19176
19177     // private
19178     onFirstFocus : function(){
19179         
19180         this.assignDocWin();
19181         
19182         
19183         this.activated = true;
19184          
19185     
19186         if(Roo.isGecko){ // prevent silly gecko errors
19187             this.win.focus();
19188             var s = this.win.getSelection();
19189             if(!s.focusNode || s.focusNode.nodeType != 3){
19190                 var r = s.getRangeAt(0);
19191                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19192                 r.collapse(true);
19193                 this.deferFocus();
19194             }
19195             try{
19196                 this.execCmd('useCSS', true);
19197                 this.execCmd('styleWithCSS', false);
19198             }catch(e){}
19199         }
19200         this.owner.fireEvent('activate', this);
19201     },
19202
19203     // private
19204     adjustFont: function(btn){
19205         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19206         //if(Roo.isSafari){ // safari
19207         //    adjust *= 2;
19208        // }
19209         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19210         if(Roo.isSafari){ // safari
19211             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19212             v =  (v < 10) ? 10 : v;
19213             v =  (v > 48) ? 48 : v;
19214             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19215             
19216         }
19217         
19218         
19219         v = Math.max(1, v+adjust);
19220         
19221         this.execCmd('FontSize', v  );
19222     },
19223
19224     onEditorEvent : function(e)
19225     {
19226         this.owner.fireEvent('editorevent', this, e);
19227       //  this.updateToolbar();
19228         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19229     },
19230
19231     insertTag : function(tg)
19232     {
19233         // could be a bit smarter... -> wrap the current selected tRoo..
19234         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19235             
19236             range = this.createRange(this.getSelection());
19237             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19238             wrappingNode.appendChild(range.extractContents());
19239             range.insertNode(wrappingNode);
19240
19241             return;
19242             
19243             
19244             
19245         }
19246         this.execCmd("formatblock",   tg);
19247         
19248     },
19249     
19250     insertText : function(txt)
19251     {
19252         
19253         
19254         var range = this.createRange();
19255         range.deleteContents();
19256                //alert(Sender.getAttribute('label'));
19257                
19258         range.insertNode(this.doc.createTextNode(txt));
19259     } ,
19260     
19261      
19262
19263     /**
19264      * Executes a Midas editor command on the editor document and performs necessary focus and
19265      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19266      * @param {String} cmd The Midas command
19267      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19268      */
19269     relayCmd : function(cmd, value){
19270         this.win.focus();
19271         this.execCmd(cmd, value);
19272         this.owner.fireEvent('editorevent', this);
19273         //this.updateToolbar();
19274         this.owner.deferFocus();
19275     },
19276
19277     /**
19278      * Executes a Midas editor command directly on the editor document.
19279      * For visual commands, you should use {@link #relayCmd} instead.
19280      * <b>This should only be called after the editor is initialized.</b>
19281      * @param {String} cmd The Midas command
19282      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19283      */
19284     execCmd : function(cmd, value){
19285         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19286         this.syncValue();
19287     },
19288  
19289  
19290    
19291     /**
19292      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19293      * to insert tRoo.
19294      * @param {String} text | dom node.. 
19295      */
19296     insertAtCursor : function(text)
19297     {
19298         
19299         
19300         
19301         if(!this.activated){
19302             return;
19303         }
19304         /*
19305         if(Roo.isIE){
19306             this.win.focus();
19307             var r = this.doc.selection.createRange();
19308             if(r){
19309                 r.collapse(true);
19310                 r.pasteHTML(text);
19311                 this.syncValue();
19312                 this.deferFocus();
19313             
19314             }
19315             return;
19316         }
19317         */
19318         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19319             this.win.focus();
19320             
19321             
19322             // from jquery ui (MIT licenced)
19323             var range, node;
19324             var win = this.win;
19325             
19326             if (win.getSelection && win.getSelection().getRangeAt) {
19327                 range = win.getSelection().getRangeAt(0);
19328                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19329                 range.insertNode(node);
19330             } else if (win.document.selection && win.document.selection.createRange) {
19331                 // no firefox support
19332                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19333                 win.document.selection.createRange().pasteHTML(txt);
19334             } else {
19335                 // no firefox support
19336                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19337                 this.execCmd('InsertHTML', txt);
19338             } 
19339             
19340             this.syncValue();
19341             
19342             this.deferFocus();
19343         }
19344     },
19345  // private
19346     mozKeyPress : function(e){
19347         if(e.ctrlKey){
19348             var c = e.getCharCode(), cmd;
19349           
19350             if(c > 0){
19351                 c = String.fromCharCode(c).toLowerCase();
19352                 switch(c){
19353                     case 'b':
19354                         cmd = 'bold';
19355                         break;
19356                     case 'i':
19357                         cmd = 'italic';
19358                         break;
19359                     
19360                     case 'u':
19361                         cmd = 'underline';
19362                         break;
19363                     
19364                     case 'v':
19365                         this.cleanUpPaste.defer(100, this);
19366                         return;
19367                         
19368                 }
19369                 if(cmd){
19370                     this.win.focus();
19371                     this.execCmd(cmd);
19372                     this.deferFocus();
19373                     e.preventDefault();
19374                 }
19375                 
19376             }
19377         }
19378     },
19379
19380     // private
19381     fixKeys : function(){ // load time branching for fastest keydown performance
19382         if(Roo.isIE){
19383             return function(e){
19384                 var k = e.getKey(), r;
19385                 if(k == e.TAB){
19386                     e.stopEvent();
19387                     r = this.doc.selection.createRange();
19388                     if(r){
19389                         r.collapse(true);
19390                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19391                         this.deferFocus();
19392                     }
19393                     return;
19394                 }
19395                 
19396                 if(k == e.ENTER){
19397                     r = this.doc.selection.createRange();
19398                     if(r){
19399                         var target = r.parentElement();
19400                         if(!target || target.tagName.toLowerCase() != 'li'){
19401                             e.stopEvent();
19402                             r.pasteHTML('<br />');
19403                             r.collapse(false);
19404                             r.select();
19405                         }
19406                     }
19407                 }
19408                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19409                     this.cleanUpPaste.defer(100, this);
19410                     return;
19411                 }
19412                 
19413                 
19414             };
19415         }else if(Roo.isOpera){
19416             return function(e){
19417                 var k = e.getKey();
19418                 if(k == e.TAB){
19419                     e.stopEvent();
19420                     this.win.focus();
19421                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19422                     this.deferFocus();
19423                 }
19424                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19425                     this.cleanUpPaste.defer(100, this);
19426                     return;
19427                 }
19428                 
19429             };
19430         }else if(Roo.isSafari){
19431             return function(e){
19432                 var k = e.getKey();
19433                 
19434                 if(k == e.TAB){
19435                     e.stopEvent();
19436                     this.execCmd('InsertText','\t');
19437                     this.deferFocus();
19438                     return;
19439                 }
19440                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19441                     this.cleanUpPaste.defer(100, this);
19442                     return;
19443                 }
19444                 
19445              };
19446         }
19447     }(),
19448     
19449     getAllAncestors: function()
19450     {
19451         var p = this.getSelectedNode();
19452         var a = [];
19453         if (!p) {
19454             a.push(p); // push blank onto stack..
19455             p = this.getParentElement();
19456         }
19457         
19458         
19459         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19460             a.push(p);
19461             p = p.parentNode;
19462         }
19463         a.push(this.doc.body);
19464         return a;
19465     },
19466     lastSel : false,
19467     lastSelNode : false,
19468     
19469     
19470     getSelection : function() 
19471     {
19472         this.assignDocWin();
19473         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19474     },
19475     
19476     getSelectedNode: function() 
19477     {
19478         // this may only work on Gecko!!!
19479         
19480         // should we cache this!!!!
19481         
19482         
19483         
19484          
19485         var range = this.createRange(this.getSelection()).cloneRange();
19486         
19487         if (Roo.isIE) {
19488             var parent = range.parentElement();
19489             while (true) {
19490                 var testRange = range.duplicate();
19491                 testRange.moveToElementText(parent);
19492                 if (testRange.inRange(range)) {
19493                     break;
19494                 }
19495                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19496                     break;
19497                 }
19498                 parent = parent.parentElement;
19499             }
19500             return parent;
19501         }
19502         
19503         // is ancestor a text element.
19504         var ac =  range.commonAncestorContainer;
19505         if (ac.nodeType == 3) {
19506             ac = ac.parentNode;
19507         }
19508         
19509         var ar = ac.childNodes;
19510          
19511         var nodes = [];
19512         var other_nodes = [];
19513         var has_other_nodes = false;
19514         for (var i=0;i<ar.length;i++) {
19515             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19516                 continue;
19517             }
19518             // fullly contained node.
19519             
19520             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19521                 nodes.push(ar[i]);
19522                 continue;
19523             }
19524             
19525             // probably selected..
19526             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19527                 other_nodes.push(ar[i]);
19528                 continue;
19529             }
19530             // outer..
19531             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19532                 continue;
19533             }
19534             
19535             
19536             has_other_nodes = true;
19537         }
19538         if (!nodes.length && other_nodes.length) {
19539             nodes= other_nodes;
19540         }
19541         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19542             return false;
19543         }
19544         
19545         return nodes[0];
19546     },
19547     createRange: function(sel)
19548     {
19549         // this has strange effects when using with 
19550         // top toolbar - not sure if it's a great idea.
19551         //this.editor.contentWindow.focus();
19552         if (typeof sel != "undefined") {
19553             try {
19554                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19555             } catch(e) {
19556                 return this.doc.createRange();
19557             }
19558         } else {
19559             return this.doc.createRange();
19560         }
19561     },
19562     getParentElement: function()
19563     {
19564         
19565         this.assignDocWin();
19566         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19567         
19568         var range = this.createRange(sel);
19569          
19570         try {
19571             var p = range.commonAncestorContainer;
19572             while (p.nodeType == 3) { // text node
19573                 p = p.parentNode;
19574             }
19575             return p;
19576         } catch (e) {
19577             return null;
19578         }
19579     
19580     },
19581     /***
19582      *
19583      * Range intersection.. the hard stuff...
19584      *  '-1' = before
19585      *  '0' = hits..
19586      *  '1' = after.
19587      *         [ -- selected range --- ]
19588      *   [fail]                        [fail]
19589      *
19590      *    basically..
19591      *      if end is before start or  hits it. fail.
19592      *      if start is after end or hits it fail.
19593      *
19594      *   if either hits (but other is outside. - then it's not 
19595      *   
19596      *    
19597      **/
19598     
19599     
19600     // @see http://www.thismuchiknow.co.uk/?p=64.
19601     rangeIntersectsNode : function(range, node)
19602     {
19603         var nodeRange = node.ownerDocument.createRange();
19604         try {
19605             nodeRange.selectNode(node);
19606         } catch (e) {
19607             nodeRange.selectNodeContents(node);
19608         }
19609     
19610         var rangeStartRange = range.cloneRange();
19611         rangeStartRange.collapse(true);
19612     
19613         var rangeEndRange = range.cloneRange();
19614         rangeEndRange.collapse(false);
19615     
19616         var nodeStartRange = nodeRange.cloneRange();
19617         nodeStartRange.collapse(true);
19618     
19619         var nodeEndRange = nodeRange.cloneRange();
19620         nodeEndRange.collapse(false);
19621     
19622         return rangeStartRange.compareBoundaryPoints(
19623                  Range.START_TO_START, nodeEndRange) == -1 &&
19624                rangeEndRange.compareBoundaryPoints(
19625                  Range.START_TO_START, nodeStartRange) == 1;
19626         
19627          
19628     },
19629     rangeCompareNode : function(range, node)
19630     {
19631         var nodeRange = node.ownerDocument.createRange();
19632         try {
19633             nodeRange.selectNode(node);
19634         } catch (e) {
19635             nodeRange.selectNodeContents(node);
19636         }
19637         
19638         
19639         range.collapse(true);
19640     
19641         nodeRange.collapse(true);
19642      
19643         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19644         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19645          
19646         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19647         
19648         var nodeIsBefore   =  ss == 1;
19649         var nodeIsAfter    = ee == -1;
19650         
19651         if (nodeIsBefore && nodeIsAfter)
19652             return 0; // outer
19653         if (!nodeIsBefore && nodeIsAfter)
19654             return 1; //right trailed.
19655         
19656         if (nodeIsBefore && !nodeIsAfter)
19657             return 2;  // left trailed.
19658         // fully contined.
19659         return 3;
19660     },
19661
19662     // private? - in a new class?
19663     cleanUpPaste :  function()
19664     {
19665         // cleans up the whole document..
19666         Roo.log('cleanuppaste');
19667         
19668         this.cleanUpChildren(this.doc.body);
19669         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19670         if (clean != this.doc.body.innerHTML) {
19671             this.doc.body.innerHTML = clean;
19672         }
19673         
19674     },
19675     
19676     cleanWordChars : function(input) {// change the chars to hex code
19677         var he = Roo.HtmlEditorCore;
19678         
19679         var output = input;
19680         Roo.each(he.swapCodes, function(sw) { 
19681             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19682             
19683             output = output.replace(swapper, sw[1]);
19684         });
19685         
19686         return output;
19687     },
19688     
19689     
19690     cleanUpChildren : function (n)
19691     {
19692         if (!n.childNodes.length) {
19693             return;
19694         }
19695         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19696            this.cleanUpChild(n.childNodes[i]);
19697         }
19698     },
19699     
19700     
19701         
19702     
19703     cleanUpChild : function (node)
19704     {
19705         var ed = this;
19706         //console.log(node);
19707         if (node.nodeName == "#text") {
19708             // clean up silly Windows -- stuff?
19709             return; 
19710         }
19711         if (node.nodeName == "#comment") {
19712             node.parentNode.removeChild(node);
19713             // clean up silly Windows -- stuff?
19714             return; 
19715         }
19716         var lcname = node.tagName.toLowerCase();
19717         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19718         // whitelist of tags..
19719         
19720         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19721             // remove node.
19722             node.parentNode.removeChild(node);
19723             return;
19724             
19725         }
19726         
19727         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19728         
19729         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19730         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19731         
19732         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19733         //    remove_keep_children = true;
19734         //}
19735         
19736         if (remove_keep_children) {
19737             this.cleanUpChildren(node);
19738             // inserts everything just before this node...
19739             while (node.childNodes.length) {
19740                 var cn = node.childNodes[0];
19741                 node.removeChild(cn);
19742                 node.parentNode.insertBefore(cn, node);
19743             }
19744             node.parentNode.removeChild(node);
19745             return;
19746         }
19747         
19748         if (!node.attributes || !node.attributes.length) {
19749             this.cleanUpChildren(node);
19750             return;
19751         }
19752         
19753         function cleanAttr(n,v)
19754         {
19755             
19756             if (v.match(/^\./) || v.match(/^\//)) {
19757                 return;
19758             }
19759             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19760                 return;
19761             }
19762             if (v.match(/^#/)) {
19763                 return;
19764             }
19765 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19766             node.removeAttribute(n);
19767             
19768         }
19769         
19770         var cwhite = this.cwhite;
19771         var cblack = this.cblack;
19772             
19773         function cleanStyle(n,v)
19774         {
19775             if (v.match(/expression/)) { //XSS?? should we even bother..
19776                 node.removeAttribute(n);
19777                 return;
19778             }
19779             
19780             var parts = v.split(/;/);
19781             var clean = [];
19782             
19783             Roo.each(parts, function(p) {
19784                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19785                 if (!p.length) {
19786                     return true;
19787                 }
19788                 var l = p.split(':').shift().replace(/\s+/g,'');
19789                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19790                 
19791                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19792 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19793                     //node.removeAttribute(n);
19794                     return true;
19795                 }
19796                 //Roo.log()
19797                 // only allow 'c whitelisted system attributes'
19798                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19799 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19800                     //node.removeAttribute(n);
19801                     return true;
19802                 }
19803                 
19804                 
19805                  
19806                 
19807                 clean.push(p);
19808                 return true;
19809             });
19810             if (clean.length) { 
19811                 node.setAttribute(n, clean.join(';'));
19812             } else {
19813                 node.removeAttribute(n);
19814             }
19815             
19816         }
19817         
19818         
19819         for (var i = node.attributes.length-1; i > -1 ; i--) {
19820             var a = node.attributes[i];
19821             //console.log(a);
19822             
19823             if (a.name.toLowerCase().substr(0,2)=='on')  {
19824                 node.removeAttribute(a.name);
19825                 continue;
19826             }
19827             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19828                 node.removeAttribute(a.name);
19829                 continue;
19830             }
19831             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19832                 cleanAttr(a.name,a.value); // fixme..
19833                 continue;
19834             }
19835             if (a.name == 'style') {
19836                 cleanStyle(a.name,a.value);
19837                 continue;
19838             }
19839             /// clean up MS crap..
19840             // tecnically this should be a list of valid class'es..
19841             
19842             
19843             if (a.name == 'class') {
19844                 if (a.value.match(/^Mso/)) {
19845                     node.className = '';
19846                 }
19847                 
19848                 if (a.value.match(/body/)) {
19849                     node.className = '';
19850                 }
19851                 continue;
19852             }
19853             
19854             // style cleanup!?
19855             // class cleanup?
19856             
19857         }
19858         
19859         
19860         this.cleanUpChildren(node);
19861         
19862         
19863     },
19864     
19865     /**
19866      * Clean up MS wordisms...
19867      */
19868     cleanWord : function(node)
19869     {
19870         
19871         
19872         if (!node) {
19873             this.cleanWord(this.doc.body);
19874             return;
19875         }
19876         if (node.nodeName == "#text") {
19877             // clean up silly Windows -- stuff?
19878             return; 
19879         }
19880         if (node.nodeName == "#comment") {
19881             node.parentNode.removeChild(node);
19882             // clean up silly Windows -- stuff?
19883             return; 
19884         }
19885         
19886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19887             node.parentNode.removeChild(node);
19888             return;
19889         }
19890         
19891         // remove - but keep children..
19892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19893             while (node.childNodes.length) {
19894                 var cn = node.childNodes[0];
19895                 node.removeChild(cn);
19896                 node.parentNode.insertBefore(cn, node);
19897             }
19898             node.parentNode.removeChild(node);
19899             this.iterateChildren(node, this.cleanWord);
19900             return;
19901         }
19902         // clean styles
19903         if (node.className.length) {
19904             
19905             var cn = node.className.split(/\W+/);
19906             var cna = [];
19907             Roo.each(cn, function(cls) {
19908                 if (cls.match(/Mso[a-zA-Z]+/)) {
19909                     return;
19910                 }
19911                 cna.push(cls);
19912             });
19913             node.className = cna.length ? cna.join(' ') : '';
19914             if (!cna.length) {
19915                 node.removeAttribute("class");
19916             }
19917         }
19918         
19919         if (node.hasAttribute("lang")) {
19920             node.removeAttribute("lang");
19921         }
19922         
19923         if (node.hasAttribute("style")) {
19924             
19925             var styles = node.getAttribute("style").split(";");
19926             var nstyle = [];
19927             Roo.each(styles, function(s) {
19928                 if (!s.match(/:/)) {
19929                     return;
19930                 }
19931                 var kv = s.split(":");
19932                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19933                     return;
19934                 }
19935                 // what ever is left... we allow.
19936                 nstyle.push(s);
19937             });
19938             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19939             if (!nstyle.length) {
19940                 node.removeAttribute('style');
19941             }
19942         }
19943         this.iterateChildren(node, this.cleanWord);
19944         
19945         
19946         
19947     },
19948     /**
19949      * iterateChildren of a Node, calling fn each time, using this as the scole..
19950      * @param {DomNode} node node to iterate children of.
19951      * @param {Function} fn method of this class to call on each item.
19952      */
19953     iterateChildren : function(node, fn)
19954     {
19955         if (!node.childNodes.length) {
19956                 return;
19957         }
19958         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19959            fn.call(this, node.childNodes[i])
19960         }
19961     },
19962     
19963     
19964     /**
19965      * cleanTableWidths.
19966      *
19967      * Quite often pasting from word etc.. results in tables with column and widths.
19968      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19969      *
19970      */
19971     cleanTableWidths : function(node)
19972     {
19973          
19974          
19975         if (!node) {
19976             this.cleanTableWidths(this.doc.body);
19977             return;
19978         }
19979         
19980         // ignore list...
19981         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19982             return; 
19983         }
19984         Roo.log(node.tagName);
19985         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
19986             this.iterateChildren(node, this.cleanTableWidths);
19987             return;
19988         }
19989         if (node.hasAttribute('width')) {
19990             node.removeAttribute('width');
19991         }
19992         
19993          
19994         if (node.hasAttribute("style")) {
19995             // pretty basic...
19996             
19997             var styles = node.getAttribute("style").split(";");
19998             var nstyle = [];
19999             Roo.each(styles, function(s) {
20000                 if (!s.match(/:/)) {
20001                     return;
20002                 }
20003                 var kv = s.split(":");
20004                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20005                     return;
20006                 }
20007                 // what ever is left... we allow.
20008                 nstyle.push(s);
20009             });
20010             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20011             if (!nstyle.length) {
20012                 node.removeAttribute('style');
20013             }
20014         }
20015         
20016         this.iterateChildren(node, this.cleanTableWidths);
20017         
20018         
20019     },
20020     
20021     
20022     
20023     
20024     domToHTML : function(currentElement, depth, nopadtext) {
20025         
20026         depth = depth || 0;
20027         nopadtext = nopadtext || false;
20028     
20029         if (!currentElement) {
20030             return this.domToHTML(this.doc.body);
20031         }
20032         
20033         //Roo.log(currentElement);
20034         var j;
20035         var allText = false;
20036         var nodeName = currentElement.nodeName;
20037         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20038         
20039         if  (nodeName == '#text') {
20040             
20041             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20042         }
20043         
20044         
20045         var ret = '';
20046         if (nodeName != 'BODY') {
20047              
20048             var i = 0;
20049             // Prints the node tagName, such as <A>, <IMG>, etc
20050             if (tagName) {
20051                 var attr = [];
20052                 for(i = 0; i < currentElement.attributes.length;i++) {
20053                     // quoting?
20054                     var aname = currentElement.attributes.item(i).name;
20055                     if (!currentElement.attributes.item(i).value.length) {
20056                         continue;
20057                     }
20058                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20059                 }
20060                 
20061                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20062             } 
20063             else {
20064                 
20065                 // eack
20066             }
20067         } else {
20068             tagName = false;
20069         }
20070         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20071             return ret;
20072         }
20073         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20074             nopadtext = true;
20075         }
20076         
20077         
20078         // Traverse the tree
20079         i = 0;
20080         var currentElementChild = currentElement.childNodes.item(i);
20081         var allText = true;
20082         var innerHTML  = '';
20083         lastnode = '';
20084         while (currentElementChild) {
20085             // Formatting code (indent the tree so it looks nice on the screen)
20086             var nopad = nopadtext;
20087             if (lastnode == 'SPAN') {
20088                 nopad  = true;
20089             }
20090             // text
20091             if  (currentElementChild.nodeName == '#text') {
20092                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20093                 toadd = nopadtext ? toadd : toadd.trim();
20094                 if (!nopad && toadd.length > 80) {
20095                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20096                 }
20097                 innerHTML  += toadd;
20098                 
20099                 i++;
20100                 currentElementChild = currentElement.childNodes.item(i);
20101                 lastNode = '';
20102                 continue;
20103             }
20104             allText = false;
20105             
20106             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20107                 
20108             // Recursively traverse the tree structure of the child node
20109             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20110             lastnode = currentElementChild.nodeName;
20111             i++;
20112             currentElementChild=currentElement.childNodes.item(i);
20113         }
20114         
20115         ret += innerHTML;
20116         
20117         if (!allText) {
20118                 // The remaining code is mostly for formatting the tree
20119             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20120         }
20121         
20122         
20123         if (tagName) {
20124             ret+= "</"+tagName+">";
20125         }
20126         return ret;
20127         
20128     },
20129         
20130     applyBlacklists : function()
20131     {
20132         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20133         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20134         
20135         this.white = [];
20136         this.black = [];
20137         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20138             if (b.indexOf(tag) > -1) {
20139                 return;
20140             }
20141             this.white.push(tag);
20142             
20143         }, this);
20144         
20145         Roo.each(w, function(tag) {
20146             if (b.indexOf(tag) > -1) {
20147                 return;
20148             }
20149             if (this.white.indexOf(tag) > -1) {
20150                 return;
20151             }
20152             this.white.push(tag);
20153             
20154         }, this);
20155         
20156         
20157         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20158             if (w.indexOf(tag) > -1) {
20159                 return;
20160             }
20161             this.black.push(tag);
20162             
20163         }, this);
20164         
20165         Roo.each(b, function(tag) {
20166             if (w.indexOf(tag) > -1) {
20167                 return;
20168             }
20169             if (this.black.indexOf(tag) > -1) {
20170                 return;
20171             }
20172             this.black.push(tag);
20173             
20174         }, this);
20175         
20176         
20177         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20178         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20179         
20180         this.cwhite = [];
20181         this.cblack = [];
20182         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20183             if (b.indexOf(tag) > -1) {
20184                 return;
20185             }
20186             this.cwhite.push(tag);
20187             
20188         }, this);
20189         
20190         Roo.each(w, function(tag) {
20191             if (b.indexOf(tag) > -1) {
20192                 return;
20193             }
20194             if (this.cwhite.indexOf(tag) > -1) {
20195                 return;
20196             }
20197             this.cwhite.push(tag);
20198             
20199         }, this);
20200         
20201         
20202         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20203             if (w.indexOf(tag) > -1) {
20204                 return;
20205             }
20206             this.cblack.push(tag);
20207             
20208         }, this);
20209         
20210         Roo.each(b, function(tag) {
20211             if (w.indexOf(tag) > -1) {
20212                 return;
20213             }
20214             if (this.cblack.indexOf(tag) > -1) {
20215                 return;
20216             }
20217             this.cblack.push(tag);
20218             
20219         }, this);
20220     },
20221     
20222     setStylesheets : function(stylesheets)
20223     {
20224         if(typeof(stylesheets) == 'string'){
20225             Roo.get(this.iframe.contentDocument.head).createChild({
20226                 tag : 'link',
20227                 rel : 'stylesheet',
20228                 type : 'text/css',
20229                 href : stylesheets
20230             });
20231             
20232             return;
20233         }
20234         var _this = this;
20235      
20236         Roo.each(stylesheets, function(s) {
20237             if(!s.length){
20238                 return;
20239             }
20240             
20241             Roo.get(_this.iframe.contentDocument.head).createChild({
20242                 tag : 'link',
20243                 rel : 'stylesheet',
20244                 type : 'text/css',
20245                 href : s
20246             });
20247         });
20248
20249         
20250     },
20251     
20252     removeStylesheets : function()
20253     {
20254         var _this = this;
20255         
20256         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20257             s.remove();
20258         });
20259     }
20260     
20261     // hide stuff that is not compatible
20262     /**
20263      * @event blur
20264      * @hide
20265      */
20266     /**
20267      * @event change
20268      * @hide
20269      */
20270     /**
20271      * @event focus
20272      * @hide
20273      */
20274     /**
20275      * @event specialkey
20276      * @hide
20277      */
20278     /**
20279      * @cfg {String} fieldClass @hide
20280      */
20281     /**
20282      * @cfg {String} focusClass @hide
20283      */
20284     /**
20285      * @cfg {String} autoCreate @hide
20286      */
20287     /**
20288      * @cfg {String} inputType @hide
20289      */
20290     /**
20291      * @cfg {String} invalidClass @hide
20292      */
20293     /**
20294      * @cfg {String} invalidText @hide
20295      */
20296     /**
20297      * @cfg {String} msgFx @hide
20298      */
20299     /**
20300      * @cfg {String} validateOnBlur @hide
20301      */
20302 });
20303
20304 Roo.HtmlEditorCore.white = [
20305         'area', 'br', 'img', 'input', 'hr', 'wbr',
20306         
20307        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20308        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20309        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20310        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20311        'table',   'ul',         'xmp', 
20312        
20313        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20314       'thead',   'tr', 
20315      
20316       'dir', 'menu', 'ol', 'ul', 'dl',
20317        
20318       'embed',  'object'
20319 ];
20320
20321
20322 Roo.HtmlEditorCore.black = [
20323     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20324         'applet', // 
20325         'base',   'basefont', 'bgsound', 'blink',  'body', 
20326         'frame',  'frameset', 'head',    'html',   'ilayer', 
20327         'iframe', 'layer',  'link',     'meta',    'object',   
20328         'script', 'style' ,'title',  'xml' // clean later..
20329 ];
20330 Roo.HtmlEditorCore.clean = [
20331     'script', 'style', 'title', 'xml'
20332 ];
20333 Roo.HtmlEditorCore.remove = [
20334     'font'
20335 ];
20336 // attributes..
20337
20338 Roo.HtmlEditorCore.ablack = [
20339     'on'
20340 ];
20341     
20342 Roo.HtmlEditorCore.aclean = [ 
20343     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20344 ];
20345
20346 // protocols..
20347 Roo.HtmlEditorCore.pwhite= [
20348         'http',  'https',  'mailto'
20349 ];
20350
20351 // white listed style attributes.
20352 Roo.HtmlEditorCore.cwhite= [
20353       //  'text-align', /// default is to allow most things..
20354       
20355          
20356 //        'font-size'//??
20357 ];
20358
20359 // black listed style attributes.
20360 Roo.HtmlEditorCore.cblack= [
20361       //  'font-size' -- this can be set by the project 
20362 ];
20363
20364
20365 Roo.HtmlEditorCore.swapCodes   =[ 
20366     [    8211, "--" ], 
20367     [    8212, "--" ], 
20368     [    8216,  "'" ],  
20369     [    8217, "'" ],  
20370     [    8220, '"' ],  
20371     [    8221, '"' ],  
20372     [    8226, "*" ],  
20373     [    8230, "..." ]
20374 ]; 
20375
20376     /*
20377  * - LGPL
20378  *
20379  * HtmlEditor
20380  * 
20381  */
20382
20383 /**
20384  * @class Roo.bootstrap.HtmlEditor
20385  * @extends Roo.bootstrap.TextArea
20386  * Bootstrap HtmlEditor class
20387
20388  * @constructor
20389  * Create a new HtmlEditor
20390  * @param {Object} config The config object
20391  */
20392
20393 Roo.bootstrap.HtmlEditor = function(config){
20394     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20395     if (!this.toolbars) {
20396         this.toolbars = [];
20397     }
20398     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20399     this.addEvents({
20400             /**
20401              * @event initialize
20402              * Fires when the editor is fully initialized (including the iframe)
20403              * @param {HtmlEditor} this
20404              */
20405             initialize: true,
20406             /**
20407              * @event activate
20408              * Fires when the editor is first receives the focus. Any insertion must wait
20409              * until after this event.
20410              * @param {HtmlEditor} this
20411              */
20412             activate: true,
20413              /**
20414              * @event beforesync
20415              * Fires before the textarea is updated with content from the editor iframe. Return false
20416              * to cancel the sync.
20417              * @param {HtmlEditor} this
20418              * @param {String} html
20419              */
20420             beforesync: true,
20421              /**
20422              * @event beforepush
20423              * Fires before the iframe editor is updated with content from the textarea. Return false
20424              * to cancel the push.
20425              * @param {HtmlEditor} this
20426              * @param {String} html
20427              */
20428             beforepush: true,
20429              /**
20430              * @event sync
20431              * Fires when the textarea is updated with content from the editor iframe.
20432              * @param {HtmlEditor} this
20433              * @param {String} html
20434              */
20435             sync: true,
20436              /**
20437              * @event push
20438              * Fires when the iframe editor is updated with content from the textarea.
20439              * @param {HtmlEditor} this
20440              * @param {String} html
20441              */
20442             push: true,
20443              /**
20444              * @event editmodechange
20445              * Fires when the editor switches edit modes
20446              * @param {HtmlEditor} this
20447              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20448              */
20449             editmodechange: true,
20450             /**
20451              * @event editorevent
20452              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20453              * @param {HtmlEditor} this
20454              */
20455             editorevent: true,
20456             /**
20457              * @event firstfocus
20458              * Fires when on first focus - needed by toolbars..
20459              * @param {HtmlEditor} this
20460              */
20461             firstfocus: true,
20462             /**
20463              * @event autosave
20464              * Auto save the htmlEditor value as a file into Events
20465              * @param {HtmlEditor} this
20466              */
20467             autosave: true,
20468             /**
20469              * @event savedpreview
20470              * preview the saved version of htmlEditor
20471              * @param {HtmlEditor} this
20472              */
20473             savedpreview: true
20474         });
20475 };
20476
20477
20478 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20479     
20480     
20481       /**
20482      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20483      */
20484     toolbars : false,
20485    
20486      /**
20487      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20488      *                        Roo.resizable.
20489      */
20490     resizable : false,
20491      /**
20492      * @cfg {Number} height (in pixels)
20493      */   
20494     height: 300,
20495    /**
20496      * @cfg {Number} width (in pixels)
20497      */   
20498     width: false,
20499     
20500     /**
20501      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20502      * 
20503      */
20504     stylesheets: false,
20505     
20506     // id of frame..
20507     frameId: false,
20508     
20509     // private properties
20510     validationEvent : false,
20511     deferHeight: true,
20512     initialized : false,
20513     activated : false,
20514     
20515     onFocus : Roo.emptyFn,
20516     iframePad:3,
20517     hideMode:'offsets',
20518     
20519     
20520     tbContainer : false,
20521     
20522     toolbarContainer :function() {
20523         return this.wrap.select('.x-html-editor-tb',true).first();
20524     },
20525
20526     /**
20527      * Protected method that will not generally be called directly. It
20528      * is called when the editor creates its toolbar. Override this method if you need to
20529      * add custom toolbar buttons.
20530      * @param {HtmlEditor} editor
20531      */
20532     createToolbar : function(){
20533         
20534         Roo.log("create toolbars");
20535         
20536         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20537         this.toolbars[0].render(this.toolbarContainer());
20538         
20539         return;
20540         
20541 //        if (!editor.toolbars || !editor.toolbars.length) {
20542 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20543 //        }
20544 //        
20545 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20546 //            editor.toolbars[i] = Roo.factory(
20547 //                    typeof(editor.toolbars[i]) == 'string' ?
20548 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20549 //                Roo.bootstrap.HtmlEditor);
20550 //            editor.toolbars[i].init(editor);
20551 //        }
20552     },
20553
20554      
20555     // private
20556     onRender : function(ct, position)
20557     {
20558        // Roo.log("Call onRender: " + this.xtype);
20559         var _t = this;
20560         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20561       
20562         this.wrap = this.inputEl().wrap({
20563             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20564         });
20565         
20566         this.editorcore.onRender(ct, position);
20567          
20568         if (this.resizable) {
20569             this.resizeEl = new Roo.Resizable(this.wrap, {
20570                 pinned : true,
20571                 wrap: true,
20572                 dynamic : true,
20573                 minHeight : this.height,
20574                 height: this.height,
20575                 handles : this.resizable,
20576                 width: this.width,
20577                 listeners : {
20578                     resize : function(r, w, h) {
20579                         _t.onResize(w,h); // -something
20580                     }
20581                 }
20582             });
20583             
20584         }
20585         this.createToolbar(this);
20586        
20587         
20588         if(!this.width && this.resizable){
20589             this.setSize(this.wrap.getSize());
20590         }
20591         if (this.resizeEl) {
20592             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20593             // should trigger onReize..
20594         }
20595         
20596     },
20597
20598     // private
20599     onResize : function(w, h)
20600     {
20601         Roo.log('resize: ' +w + ',' + h );
20602         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20603         var ew = false;
20604         var eh = false;
20605         
20606         if(this.inputEl() ){
20607             if(typeof w == 'number'){
20608                 var aw = w - this.wrap.getFrameWidth('lr');
20609                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20610                 ew = aw;
20611             }
20612             if(typeof h == 'number'){
20613                  var tbh = -11;  // fixme it needs to tool bar size!
20614                 for (var i =0; i < this.toolbars.length;i++) {
20615                     // fixme - ask toolbars for heights?
20616                     tbh += this.toolbars[i].el.getHeight();
20617                     //if (this.toolbars[i].footer) {
20618                     //    tbh += this.toolbars[i].footer.el.getHeight();
20619                     //}
20620                 }
20621               
20622                 
20623                 
20624                 
20625                 
20626                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20627                 ah -= 5; // knock a few pixes off for look..
20628                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20629                 var eh = ah;
20630             }
20631         }
20632         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20633         this.editorcore.onResize(ew,eh);
20634         
20635     },
20636
20637     /**
20638      * Toggles the editor between standard and source edit mode.
20639      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20640      */
20641     toggleSourceEdit : function(sourceEditMode)
20642     {
20643         this.editorcore.toggleSourceEdit(sourceEditMode);
20644         
20645         if(this.editorcore.sourceEditMode){
20646             Roo.log('editor - showing textarea');
20647             
20648 //            Roo.log('in');
20649 //            Roo.log(this.syncValue());
20650             this.syncValue();
20651             this.inputEl().removeClass(['hide', 'x-hidden']);
20652             this.inputEl().dom.removeAttribute('tabIndex');
20653             this.inputEl().focus();
20654         }else{
20655             Roo.log('editor - hiding textarea');
20656 //            Roo.log('out')
20657 //            Roo.log(this.pushValue()); 
20658             this.pushValue();
20659             
20660             this.inputEl().addClass(['hide', 'x-hidden']);
20661             this.inputEl().dom.setAttribute('tabIndex', -1);
20662             //this.deferFocus();
20663         }
20664          
20665         if(this.resizable){
20666             this.setSize(this.wrap.getSize());
20667         }
20668         
20669         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20670     },
20671  
20672     // private (for BoxComponent)
20673     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20674
20675     // private (for BoxComponent)
20676     getResizeEl : function(){
20677         return this.wrap;
20678     },
20679
20680     // private (for BoxComponent)
20681     getPositionEl : function(){
20682         return this.wrap;
20683     },
20684
20685     // private
20686     initEvents : function(){
20687         this.originalValue = this.getValue();
20688     },
20689
20690 //    /**
20691 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20692 //     * @method
20693 //     */
20694 //    markInvalid : Roo.emptyFn,
20695 //    /**
20696 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20697 //     * @method
20698 //     */
20699 //    clearInvalid : Roo.emptyFn,
20700
20701     setValue : function(v){
20702         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20703         this.editorcore.pushValue();
20704     },
20705
20706      
20707     // private
20708     deferFocus : function(){
20709         this.focus.defer(10, this);
20710     },
20711
20712     // doc'ed in Field
20713     focus : function(){
20714         this.editorcore.focus();
20715         
20716     },
20717       
20718
20719     // private
20720     onDestroy : function(){
20721         
20722         
20723         
20724         if(this.rendered){
20725             
20726             for (var i =0; i < this.toolbars.length;i++) {
20727                 // fixme - ask toolbars for heights?
20728                 this.toolbars[i].onDestroy();
20729             }
20730             
20731             this.wrap.dom.innerHTML = '';
20732             this.wrap.remove();
20733         }
20734     },
20735
20736     // private
20737     onFirstFocus : function(){
20738         //Roo.log("onFirstFocus");
20739         this.editorcore.onFirstFocus();
20740          for (var i =0; i < this.toolbars.length;i++) {
20741             this.toolbars[i].onFirstFocus();
20742         }
20743         
20744     },
20745     
20746     // private
20747     syncValue : function()
20748     {   
20749         this.editorcore.syncValue();
20750     },
20751     
20752     pushValue : function()
20753     {   
20754         this.editorcore.pushValue();
20755     }
20756      
20757     
20758     // hide stuff that is not compatible
20759     /**
20760      * @event blur
20761      * @hide
20762      */
20763     /**
20764      * @event change
20765      * @hide
20766      */
20767     /**
20768      * @event focus
20769      * @hide
20770      */
20771     /**
20772      * @event specialkey
20773      * @hide
20774      */
20775     /**
20776      * @cfg {String} fieldClass @hide
20777      */
20778     /**
20779      * @cfg {String} focusClass @hide
20780      */
20781     /**
20782      * @cfg {String} autoCreate @hide
20783      */
20784     /**
20785      * @cfg {String} inputType @hide
20786      */
20787     /**
20788      * @cfg {String} invalidClass @hide
20789      */
20790     /**
20791      * @cfg {String} invalidText @hide
20792      */
20793     /**
20794      * @cfg {String} msgFx @hide
20795      */
20796     /**
20797      * @cfg {String} validateOnBlur @hide
20798      */
20799 });
20800  
20801     
20802    
20803    
20804    
20805       
20806 Roo.namespace('Roo.bootstrap.htmleditor');
20807 /**
20808  * @class Roo.bootstrap.HtmlEditorToolbar1
20809  * Basic Toolbar
20810  * 
20811  * Usage:
20812  *
20813  new Roo.bootstrap.HtmlEditor({
20814     ....
20815     toolbars : [
20816         new Roo.bootstrap.HtmlEditorToolbar1({
20817             disable : { fonts: 1 , format: 1, ..., ... , ...],
20818             btns : [ .... ]
20819         })
20820     }
20821      
20822  * 
20823  * @cfg {Object} disable List of elements to disable..
20824  * @cfg {Array} btns List of additional buttons.
20825  * 
20826  * 
20827  * NEEDS Extra CSS? 
20828  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20829  */
20830  
20831 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20832 {
20833     
20834     Roo.apply(this, config);
20835     
20836     // default disabled, based on 'good practice'..
20837     this.disable = this.disable || {};
20838     Roo.applyIf(this.disable, {
20839         fontSize : true,
20840         colors : true,
20841         specialElements : true
20842     });
20843     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20844     
20845     this.editor = config.editor;
20846     this.editorcore = config.editor.editorcore;
20847     
20848     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20849     
20850     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20851     // dont call parent... till later.
20852 }
20853 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20854      
20855     bar : true,
20856     
20857     editor : false,
20858     editorcore : false,
20859     
20860     
20861     formats : [
20862         "p" ,  
20863         "h1","h2","h3","h4","h5","h6", 
20864         "pre", "code", 
20865         "abbr", "acronym", "address", "cite", "samp", "var",
20866         'div','span'
20867     ],
20868     
20869     onRender : function(ct, position)
20870     {
20871        // Roo.log("Call onRender: " + this.xtype);
20872         
20873        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20874        Roo.log(this.el);
20875        this.el.dom.style.marginBottom = '0';
20876        var _this = this;
20877        var editorcore = this.editorcore;
20878        var editor= this.editor;
20879        
20880        var children = [];
20881        var btn = function(id,cmd , toggle, handler){
20882        
20883             var  event = toggle ? 'toggle' : 'click';
20884        
20885             var a = {
20886                 size : 'sm',
20887                 xtype: 'Button',
20888                 xns: Roo.bootstrap,
20889                 glyphicon : id,
20890                 cmd : id || cmd,
20891                 enableToggle:toggle !== false,
20892                 //html : 'submit'
20893                 pressed : toggle ? false : null,
20894                 listeners : {}
20895             }
20896             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20897                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20898             }
20899             children.push(a);
20900             return a;
20901        }
20902         
20903         var style = {
20904                 xtype: 'Button',
20905                 size : 'sm',
20906                 xns: Roo.bootstrap,
20907                 glyphicon : 'font',
20908                 //html : 'submit'
20909                 menu : {
20910                     xtype: 'Menu',
20911                     xns: Roo.bootstrap,
20912                     items:  []
20913                 }
20914         };
20915         Roo.each(this.formats, function(f) {
20916             style.menu.items.push({
20917                 xtype :'MenuItem',
20918                 xns: Roo.bootstrap,
20919                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20920                 tagname : f,
20921                 listeners : {
20922                     click : function()
20923                     {
20924                         editorcore.insertTag(this.tagname);
20925                         editor.focus();
20926                     }
20927                 }
20928                 
20929             });
20930         });
20931          children.push(style);   
20932             
20933             
20934         btn('bold',false,true);
20935         btn('italic',false,true);
20936         btn('align-left', 'justifyleft',true);
20937         btn('align-center', 'justifycenter',true);
20938         btn('align-right' , 'justifyright',true);
20939         btn('link', false, false, function(btn) {
20940             //Roo.log("create link?");
20941             var url = prompt(this.createLinkText, this.defaultLinkValue);
20942             if(url && url != 'http:/'+'/'){
20943                 this.editorcore.relayCmd('createlink', url);
20944             }
20945         }),
20946         btn('list','insertunorderedlist',true);
20947         btn('pencil', false,true, function(btn){
20948                 Roo.log(this);
20949                 
20950                 this.toggleSourceEdit(btn.pressed);
20951         });
20952         /*
20953         var cog = {
20954                 xtype: 'Button',
20955                 size : 'sm',
20956                 xns: Roo.bootstrap,
20957                 glyphicon : 'cog',
20958                 //html : 'submit'
20959                 menu : {
20960                     xtype: 'Menu',
20961                     xns: Roo.bootstrap,
20962                     items:  []
20963                 }
20964         };
20965         
20966         cog.menu.items.push({
20967             xtype :'MenuItem',
20968             xns: Roo.bootstrap,
20969             html : Clean styles,
20970             tagname : f,
20971             listeners : {
20972                 click : function()
20973                 {
20974                     editorcore.insertTag(this.tagname);
20975                     editor.focus();
20976                 }
20977             }
20978             
20979         });
20980        */
20981         
20982          
20983        this.xtype = 'NavSimplebar';
20984         
20985         for(var i=0;i< children.length;i++) {
20986             
20987             this.buttons.add(this.addxtypeChild(children[i]));
20988             
20989         }
20990         
20991         editor.on('editorevent', this.updateToolbar, this);
20992     },
20993     onBtnClick : function(id)
20994     {
20995        this.editorcore.relayCmd(id);
20996        this.editorcore.focus();
20997     },
20998     
20999     /**
21000      * Protected method that will not generally be called directly. It triggers
21001      * a toolbar update by reading the markup state of the current selection in the editor.
21002      */
21003     updateToolbar: function(){
21004
21005         if(!this.editorcore.activated){
21006             this.editor.onFirstFocus(); // is this neeed?
21007             return;
21008         }
21009
21010         var btns = this.buttons; 
21011         var doc = this.editorcore.doc;
21012         btns.get('bold').setActive(doc.queryCommandState('bold'));
21013         btns.get('italic').setActive(doc.queryCommandState('italic'));
21014         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21015         
21016         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21017         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21018         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21019         
21020         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21021         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21022          /*
21023         
21024         var ans = this.editorcore.getAllAncestors();
21025         if (this.formatCombo) {
21026             
21027             
21028             var store = this.formatCombo.store;
21029             this.formatCombo.setValue("");
21030             for (var i =0; i < ans.length;i++) {
21031                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21032                     // select it..
21033                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21034                     break;
21035                 }
21036             }
21037         }
21038         
21039         
21040         
21041         // hides menus... - so this cant be on a menu...
21042         Roo.bootstrap.MenuMgr.hideAll();
21043         */
21044         Roo.bootstrap.MenuMgr.hideAll();
21045         //this.editorsyncValue();
21046     },
21047     onFirstFocus: function() {
21048         this.buttons.each(function(item){
21049            item.enable();
21050         });
21051     },
21052     toggleSourceEdit : function(sourceEditMode){
21053         
21054           
21055         if(sourceEditMode){
21056             Roo.log("disabling buttons");
21057            this.buttons.each( function(item){
21058                 if(item.cmd != 'pencil'){
21059                     item.disable();
21060                 }
21061             });
21062           
21063         }else{
21064             Roo.log("enabling buttons");
21065             if(this.editorcore.initialized){
21066                 this.buttons.each( function(item){
21067                     item.enable();
21068                 });
21069             }
21070             
21071         }
21072         Roo.log("calling toggole on editor");
21073         // tell the editor that it's been pressed..
21074         this.editor.toggleSourceEdit(sourceEditMode);
21075        
21076     }
21077 });
21078
21079
21080
21081
21082
21083 /**
21084  * @class Roo.bootstrap.Table.AbstractSelectionModel
21085  * @extends Roo.util.Observable
21086  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21087  * implemented by descendant classes.  This class should not be directly instantiated.
21088  * @constructor
21089  */
21090 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21091     this.locked = false;
21092     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21093 };
21094
21095
21096 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21097     /** @ignore Called by the grid automatically. Do not call directly. */
21098     init : function(grid){
21099         this.grid = grid;
21100         this.initEvents();
21101     },
21102
21103     /**
21104      * Locks the selections.
21105      */
21106     lock : function(){
21107         this.locked = true;
21108     },
21109
21110     /**
21111      * Unlocks the selections.
21112      */
21113     unlock : function(){
21114         this.locked = false;
21115     },
21116
21117     /**
21118      * Returns true if the selections are locked.
21119      * @return {Boolean}
21120      */
21121     isLocked : function(){
21122         return this.locked;
21123     }
21124 });
21125 /**
21126  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21127  * @class Roo.bootstrap.Table.RowSelectionModel
21128  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21129  * It supports multiple selections and keyboard selection/navigation. 
21130  * @constructor
21131  * @param {Object} config
21132  */
21133
21134 Roo.bootstrap.Table.RowSelectionModel = function(config){
21135     Roo.apply(this, config);
21136     this.selections = new Roo.util.MixedCollection(false, function(o){
21137         return o.id;
21138     });
21139
21140     this.last = false;
21141     this.lastActive = false;
21142
21143     this.addEvents({
21144         /**
21145              * @event selectionchange
21146              * Fires when the selection changes
21147              * @param {SelectionModel} this
21148              */
21149             "selectionchange" : true,
21150         /**
21151              * @event afterselectionchange
21152              * Fires after the selection changes (eg. by key press or clicking)
21153              * @param {SelectionModel} this
21154              */
21155             "afterselectionchange" : true,
21156         /**
21157              * @event beforerowselect
21158              * Fires when a row is selected being selected, return false to cancel.
21159              * @param {SelectionModel} this
21160              * @param {Number} rowIndex The selected index
21161              * @param {Boolean} keepExisting False if other selections will be cleared
21162              */
21163             "beforerowselect" : true,
21164         /**
21165              * @event rowselect
21166              * Fires when a row is selected.
21167              * @param {SelectionModel} this
21168              * @param {Number} rowIndex The selected index
21169              * @param {Roo.data.Record} r The record
21170              */
21171             "rowselect" : true,
21172         /**
21173              * @event rowdeselect
21174              * Fires when a row is deselected.
21175              * @param {SelectionModel} this
21176              * @param {Number} rowIndex The selected index
21177              */
21178         "rowdeselect" : true
21179     });
21180     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21181     this.locked = false;
21182 };
21183
21184 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21185     /**
21186      * @cfg {Boolean} singleSelect
21187      * True to allow selection of only one row at a time (defaults to false)
21188      */
21189     singleSelect : false,
21190
21191     // private
21192     initEvents : function(){
21193
21194         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21195             this.grid.on("mousedown", this.handleMouseDown, this);
21196         }else{ // allow click to work like normal
21197             this.grid.on("rowclick", this.handleDragableRowClick, this);
21198         }
21199
21200         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21201             "up" : function(e){
21202                 if(!e.shiftKey){
21203                     this.selectPrevious(e.shiftKey);
21204                 }else if(this.last !== false && this.lastActive !== false){
21205                     var last = this.last;
21206                     this.selectRange(this.last,  this.lastActive-1);
21207                     this.grid.getView().focusRow(this.lastActive);
21208                     if(last !== false){
21209                         this.last = last;
21210                     }
21211                 }else{
21212                     this.selectFirstRow();
21213                 }
21214                 this.fireEvent("afterselectionchange", this);
21215             },
21216             "down" : function(e){
21217                 if(!e.shiftKey){
21218                     this.selectNext(e.shiftKey);
21219                 }else if(this.last !== false && this.lastActive !== false){
21220                     var last = this.last;
21221                     this.selectRange(this.last,  this.lastActive+1);
21222                     this.grid.getView().focusRow(this.lastActive);
21223                     if(last !== false){
21224                         this.last = last;
21225                     }
21226                 }else{
21227                     this.selectFirstRow();
21228                 }
21229                 this.fireEvent("afterselectionchange", this);
21230             },
21231             scope: this
21232         });
21233
21234         var view = this.grid.view;
21235         view.on("refresh", this.onRefresh, this);
21236         view.on("rowupdated", this.onRowUpdated, this);
21237         view.on("rowremoved", this.onRemove, this);
21238     },
21239
21240     // private
21241     onRefresh : function(){
21242         var ds = this.grid.dataSource, i, v = this.grid.view;
21243         var s = this.selections;
21244         s.each(function(r){
21245             if((i = ds.indexOfId(r.id)) != -1){
21246                 v.onRowSelect(i);
21247             }else{
21248                 s.remove(r);
21249             }
21250         });
21251     },
21252
21253     // private
21254     onRemove : function(v, index, r){
21255         this.selections.remove(r);
21256     },
21257
21258     // private
21259     onRowUpdated : function(v, index, r){
21260         if(this.isSelected(r)){
21261             v.onRowSelect(index);
21262         }
21263     },
21264
21265     /**
21266      * Select records.
21267      * @param {Array} records The records to select
21268      * @param {Boolean} keepExisting (optional) True to keep existing selections
21269      */
21270     selectRecords : function(records, keepExisting){
21271         if(!keepExisting){
21272             this.clearSelections();
21273         }
21274         var ds = this.grid.dataSource;
21275         for(var i = 0, len = records.length; i < len; i++){
21276             this.selectRow(ds.indexOf(records[i]), true);
21277         }
21278     },
21279
21280     /**
21281      * Gets the number of selected rows.
21282      * @return {Number}
21283      */
21284     getCount : function(){
21285         return this.selections.length;
21286     },
21287
21288     /**
21289      * Selects the first row in the grid.
21290      */
21291     selectFirstRow : function(){
21292         this.selectRow(0);
21293     },
21294
21295     /**
21296      * Select the last row.
21297      * @param {Boolean} keepExisting (optional) True to keep existing selections
21298      */
21299     selectLastRow : function(keepExisting){
21300         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21301     },
21302
21303     /**
21304      * Selects the row immediately following the last selected row.
21305      * @param {Boolean} keepExisting (optional) True to keep existing selections
21306      */
21307     selectNext : function(keepExisting){
21308         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21309             this.selectRow(this.last+1, keepExisting);
21310             this.grid.getView().focusRow(this.last);
21311         }
21312     },
21313
21314     /**
21315      * Selects the row that precedes the last selected row.
21316      * @param {Boolean} keepExisting (optional) True to keep existing selections
21317      */
21318     selectPrevious : function(keepExisting){
21319         if(this.last){
21320             this.selectRow(this.last-1, keepExisting);
21321             this.grid.getView().focusRow(this.last);
21322         }
21323     },
21324
21325     /**
21326      * Returns the selected records
21327      * @return {Array} Array of selected records
21328      */
21329     getSelections : function(){
21330         return [].concat(this.selections.items);
21331     },
21332
21333     /**
21334      * Returns the first selected record.
21335      * @return {Record}
21336      */
21337     getSelected : function(){
21338         return this.selections.itemAt(0);
21339     },
21340
21341
21342     /**
21343      * Clears all selections.
21344      */
21345     clearSelections : function(fast){
21346         if(this.locked) return;
21347         if(fast !== true){
21348             var ds = this.grid.dataSource;
21349             var s = this.selections;
21350             s.each(function(r){
21351                 this.deselectRow(ds.indexOfId(r.id));
21352             }, this);
21353             s.clear();
21354         }else{
21355             this.selections.clear();
21356         }
21357         this.last = false;
21358     },
21359
21360
21361     /**
21362      * Selects all rows.
21363      */
21364     selectAll : function(){
21365         if(this.locked) return;
21366         this.selections.clear();
21367         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21368             this.selectRow(i, true);
21369         }
21370     },
21371
21372     /**
21373      * Returns True if there is a selection.
21374      * @return {Boolean}
21375      */
21376     hasSelection : function(){
21377         return this.selections.length > 0;
21378     },
21379
21380     /**
21381      * Returns True if the specified row is selected.
21382      * @param {Number/Record} record The record or index of the record to check
21383      * @return {Boolean}
21384      */
21385     isSelected : function(index){
21386         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21387         return (r && this.selections.key(r.id) ? true : false);
21388     },
21389
21390     /**
21391      * Returns True if the specified record id is selected.
21392      * @param {String} id The id of record to check
21393      * @return {Boolean}
21394      */
21395     isIdSelected : function(id){
21396         return (this.selections.key(id) ? true : false);
21397     },
21398
21399     // private
21400     handleMouseDown : function(e, t){
21401         var view = this.grid.getView(), rowIndex;
21402         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21403             return;
21404         };
21405         if(e.shiftKey && this.last !== false){
21406             var last = this.last;
21407             this.selectRange(last, rowIndex, e.ctrlKey);
21408             this.last = last; // reset the last
21409             view.focusRow(rowIndex);
21410         }else{
21411             var isSelected = this.isSelected(rowIndex);
21412             if(e.button !== 0 && isSelected){
21413                 view.focusRow(rowIndex);
21414             }else if(e.ctrlKey && isSelected){
21415                 this.deselectRow(rowIndex);
21416             }else if(!isSelected){
21417                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21418                 view.focusRow(rowIndex);
21419             }
21420         }
21421         this.fireEvent("afterselectionchange", this);
21422     },
21423     // private
21424     handleDragableRowClick :  function(grid, rowIndex, e) 
21425     {
21426         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21427             this.selectRow(rowIndex, false);
21428             grid.view.focusRow(rowIndex);
21429              this.fireEvent("afterselectionchange", this);
21430         }
21431     },
21432     
21433     /**
21434      * Selects multiple rows.
21435      * @param {Array} rows Array of the indexes of the row to select
21436      * @param {Boolean} keepExisting (optional) True to keep existing selections
21437      */
21438     selectRows : function(rows, keepExisting){
21439         if(!keepExisting){
21440             this.clearSelections();
21441         }
21442         for(var i = 0, len = rows.length; i < len; i++){
21443             this.selectRow(rows[i], true);
21444         }
21445     },
21446
21447     /**
21448      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21449      * @param {Number} startRow The index of the first row in the range
21450      * @param {Number} endRow The index of the last row in the range
21451      * @param {Boolean} keepExisting (optional) True to retain existing selections
21452      */
21453     selectRange : function(startRow, endRow, keepExisting){
21454         if(this.locked) return;
21455         if(!keepExisting){
21456             this.clearSelections();
21457         }
21458         if(startRow <= endRow){
21459             for(var i = startRow; i <= endRow; i++){
21460                 this.selectRow(i, true);
21461             }
21462         }else{
21463             for(var i = startRow; i >= endRow; i--){
21464                 this.selectRow(i, true);
21465             }
21466         }
21467     },
21468
21469     /**
21470      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21471      * @param {Number} startRow The index of the first row in the range
21472      * @param {Number} endRow The index of the last row in the range
21473      */
21474     deselectRange : function(startRow, endRow, preventViewNotify){
21475         if(this.locked) return;
21476         for(var i = startRow; i <= endRow; i++){
21477             this.deselectRow(i, preventViewNotify);
21478         }
21479     },
21480
21481     /**
21482      * Selects a row.
21483      * @param {Number} row The index of the row to select
21484      * @param {Boolean} keepExisting (optional) True to keep existing selections
21485      */
21486     selectRow : function(index, keepExisting, preventViewNotify){
21487         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21488         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21489             if(!keepExisting || this.singleSelect){
21490                 this.clearSelections();
21491             }
21492             var r = this.grid.dataSource.getAt(index);
21493             this.selections.add(r);
21494             this.last = this.lastActive = index;
21495             if(!preventViewNotify){
21496                 this.grid.getView().onRowSelect(index);
21497             }
21498             this.fireEvent("rowselect", this, index, r);
21499             this.fireEvent("selectionchange", this);
21500         }
21501     },
21502
21503     /**
21504      * Deselects a row.
21505      * @param {Number} row The index of the row to deselect
21506      */
21507     deselectRow : function(index, preventViewNotify){
21508         if(this.locked) return;
21509         if(this.last == index){
21510             this.last = false;
21511         }
21512         if(this.lastActive == index){
21513             this.lastActive = false;
21514         }
21515         var r = this.grid.dataSource.getAt(index);
21516         this.selections.remove(r);
21517         if(!preventViewNotify){
21518             this.grid.getView().onRowDeselect(index);
21519         }
21520         this.fireEvent("rowdeselect", this, index);
21521         this.fireEvent("selectionchange", this);
21522     },
21523
21524     // private
21525     restoreLast : function(){
21526         if(this._last){
21527             this.last = this._last;
21528         }
21529     },
21530
21531     // private
21532     acceptsNav : function(row, col, cm){
21533         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21534     },
21535
21536     // private
21537     onEditorKey : function(field, e){
21538         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21539         if(k == e.TAB){
21540             e.stopEvent();
21541             ed.completeEdit();
21542             if(e.shiftKey){
21543                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21544             }else{
21545                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21546             }
21547         }else if(k == e.ENTER && !e.ctrlKey){
21548             e.stopEvent();
21549             ed.completeEdit();
21550             if(e.shiftKey){
21551                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21552             }else{
21553                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21554             }
21555         }else if(k == e.ESC){
21556             ed.cancelEdit();
21557         }
21558         if(newCell){
21559             g.startEditing(newCell[0], newCell[1]);
21560         }
21561     }
21562 });/*
21563  * Based on:
21564  * Ext JS Library 1.1.1
21565  * Copyright(c) 2006-2007, Ext JS, LLC.
21566  *
21567  * Originally Released Under LGPL - original licence link has changed is not relivant.
21568  *
21569  * Fork - LGPL
21570  * <script type="text/javascript">
21571  */
21572  
21573 /**
21574  * @class Roo.bootstrap.PagingToolbar
21575  * @extends Roo.Row
21576  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21577  * @constructor
21578  * Create a new PagingToolbar
21579  * @param {Object} config The config object
21580  */
21581 Roo.bootstrap.PagingToolbar = function(config)
21582 {
21583     // old args format still supported... - xtype is prefered..
21584         // created from xtype...
21585     var ds = config.dataSource;
21586     this.toolbarItems = [];
21587     if (config.items) {
21588         this.toolbarItems = config.items;
21589 //        config.items = [];
21590     }
21591     
21592     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21593     this.ds = ds;
21594     this.cursor = 0;
21595     if (ds) { 
21596         this.bind(ds);
21597     }
21598     
21599     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21600     
21601 };
21602
21603 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21604     /**
21605      * @cfg {Roo.data.Store} dataSource
21606      * The underlying data store providing the paged data
21607      */
21608     /**
21609      * @cfg {String/HTMLElement/Element} container
21610      * container The id or element that will contain the toolbar
21611      */
21612     /**
21613      * @cfg {Boolean} displayInfo
21614      * True to display the displayMsg (defaults to false)
21615      */
21616     /**
21617      * @cfg {Number} pageSize
21618      * The number of records to display per page (defaults to 20)
21619      */
21620     pageSize: 20,
21621     /**
21622      * @cfg {String} displayMsg
21623      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21624      */
21625     displayMsg : 'Displaying {0} - {1} of {2}',
21626     /**
21627      * @cfg {String} emptyMsg
21628      * The message to display when no records are found (defaults to "No data to display")
21629      */
21630     emptyMsg : 'No data to display',
21631     /**
21632      * Customizable piece of the default paging text (defaults to "Page")
21633      * @type String
21634      */
21635     beforePageText : "Page",
21636     /**
21637      * Customizable piece of the default paging text (defaults to "of %0")
21638      * @type String
21639      */
21640     afterPageText : "of {0}",
21641     /**
21642      * Customizable piece of the default paging text (defaults to "First Page")
21643      * @type String
21644      */
21645     firstText : "First Page",
21646     /**
21647      * Customizable piece of the default paging text (defaults to "Previous Page")
21648      * @type String
21649      */
21650     prevText : "Previous Page",
21651     /**
21652      * Customizable piece of the default paging text (defaults to "Next Page")
21653      * @type String
21654      */
21655     nextText : "Next Page",
21656     /**
21657      * Customizable piece of the default paging text (defaults to "Last Page")
21658      * @type String
21659      */
21660     lastText : "Last Page",
21661     /**
21662      * Customizable piece of the default paging text (defaults to "Refresh")
21663      * @type String
21664      */
21665     refreshText : "Refresh",
21666
21667     buttons : false,
21668     // private
21669     onRender : function(ct, position) 
21670     {
21671         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21672         this.navgroup.parentId = this.id;
21673         this.navgroup.onRender(this.el, null);
21674         // add the buttons to the navgroup
21675         
21676         if(this.displayInfo){
21677             Roo.log(this.el.select('ul.navbar-nav',true).first());
21678             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21679             this.displayEl = this.el.select('.x-paging-info', true).first();
21680 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21681 //            this.displayEl = navel.el.select('span',true).first();
21682         }
21683         
21684         var _this = this;
21685         
21686         if(this.buttons){
21687             Roo.each(_this.buttons, function(e){
21688                Roo.factory(e).onRender(_this.el, null);
21689             });
21690         }
21691             
21692         Roo.each(_this.toolbarItems, function(e) {
21693             _this.navgroup.addItem(e);
21694         });
21695         
21696         
21697         this.first = this.navgroup.addItem({
21698             tooltip: this.firstText,
21699             cls: "prev",
21700             icon : 'fa fa-backward',
21701             disabled: true,
21702             preventDefault: true,
21703             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21704         });
21705         
21706         this.prev =  this.navgroup.addItem({
21707             tooltip: this.prevText,
21708             cls: "prev",
21709             icon : 'fa fa-step-backward',
21710             disabled: true,
21711             preventDefault: true,
21712             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21713         });
21714     //this.addSeparator();
21715         
21716         
21717         var field = this.navgroup.addItem( {
21718             tagtype : 'span',
21719             cls : 'x-paging-position',
21720             
21721             html : this.beforePageText  +
21722                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21723                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21724          } ); //?? escaped?
21725         
21726         this.field = field.el.select('input', true).first();
21727         this.field.on("keydown", this.onPagingKeydown, this);
21728         this.field.on("focus", function(){this.dom.select();});
21729     
21730     
21731         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21732         //this.field.setHeight(18);
21733         //this.addSeparator();
21734         this.next = this.navgroup.addItem({
21735             tooltip: this.nextText,
21736             cls: "next",
21737             html : ' <i class="fa fa-step-forward">',
21738             disabled: true,
21739             preventDefault: true,
21740             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21741         });
21742         this.last = this.navgroup.addItem({
21743             tooltip: this.lastText,
21744             icon : 'fa fa-forward',
21745             cls: "next",
21746             disabled: true,
21747             preventDefault: true,
21748             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21749         });
21750     //this.addSeparator();
21751         this.loading = this.navgroup.addItem({
21752             tooltip: this.refreshText,
21753             icon: 'fa fa-refresh',
21754             preventDefault: true,
21755             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21756         });
21757
21758     },
21759
21760     // private
21761     updateInfo : function(){
21762         if(this.displayEl){
21763             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21764             var msg = count == 0 ?
21765                 this.emptyMsg :
21766                 String.format(
21767                     this.displayMsg,
21768                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21769                 );
21770             this.displayEl.update(msg);
21771         }
21772     },
21773
21774     // private
21775     onLoad : function(ds, r, o){
21776        this.cursor = o.params ? o.params.start : 0;
21777        var d = this.getPageData(),
21778             ap = d.activePage,
21779             ps = d.pages;
21780         
21781        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21782        this.field.dom.value = ap;
21783        this.first.setDisabled(ap == 1);
21784        this.prev.setDisabled(ap == 1);
21785        this.next.setDisabled(ap == ps);
21786        this.last.setDisabled(ap == ps);
21787        this.loading.enable();
21788        this.updateInfo();
21789     },
21790
21791     // private
21792     getPageData : function(){
21793         var total = this.ds.getTotalCount();
21794         return {
21795             total : total,
21796             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21797             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21798         };
21799     },
21800
21801     // private
21802     onLoadError : function(){
21803         this.loading.enable();
21804     },
21805
21806     // private
21807     onPagingKeydown : function(e){
21808         var k = e.getKey();
21809         var d = this.getPageData();
21810         if(k == e.RETURN){
21811             var v = this.field.dom.value, pageNum;
21812             if(!v || isNaN(pageNum = parseInt(v, 10))){
21813                 this.field.dom.value = d.activePage;
21814                 return;
21815             }
21816             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21817             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21818             e.stopEvent();
21819         }
21820         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
21821         {
21822           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21823           this.field.dom.value = pageNum;
21824           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21825           e.stopEvent();
21826         }
21827         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21828         {
21829           var v = this.field.dom.value, pageNum; 
21830           var increment = (e.shiftKey) ? 10 : 1;
21831           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21832             increment *= -1;
21833           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21834             this.field.dom.value = d.activePage;
21835             return;
21836           }
21837           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21838           {
21839             this.field.dom.value = parseInt(v, 10) + increment;
21840             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21841             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21842           }
21843           e.stopEvent();
21844         }
21845     },
21846
21847     // private
21848     beforeLoad : function(){
21849         if(this.loading){
21850             this.loading.disable();
21851         }
21852     },
21853
21854     // private
21855     onClick : function(which){
21856         
21857         var ds = this.ds;
21858         if (!ds) {
21859             return;
21860         }
21861         
21862         switch(which){
21863             case "first":
21864                 ds.load({params:{start: 0, limit: this.pageSize}});
21865             break;
21866             case "prev":
21867                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21868             break;
21869             case "next":
21870                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21871             break;
21872             case "last":
21873                 var total = ds.getTotalCount();
21874                 var extra = total % this.pageSize;
21875                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21876                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21877             break;
21878             case "refresh":
21879                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21880             break;
21881         }
21882     },
21883
21884     /**
21885      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21886      * @param {Roo.data.Store} store The data store to unbind
21887      */
21888     unbind : function(ds){
21889         ds.un("beforeload", this.beforeLoad, this);
21890         ds.un("load", this.onLoad, this);
21891         ds.un("loadexception", this.onLoadError, this);
21892         ds.un("remove", this.updateInfo, this);
21893         ds.un("add", this.updateInfo, this);
21894         this.ds = undefined;
21895     },
21896
21897     /**
21898      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21899      * @param {Roo.data.Store} store The data store to bind
21900      */
21901     bind : function(ds){
21902         ds.on("beforeload", this.beforeLoad, this);
21903         ds.on("load", this.onLoad, this);
21904         ds.on("loadexception", this.onLoadError, this);
21905         ds.on("remove", this.updateInfo, this);
21906         ds.on("add", this.updateInfo, this);
21907         this.ds = ds;
21908     }
21909 });/*
21910  * - LGPL
21911  *
21912  * element
21913  * 
21914  */
21915
21916 /**
21917  * @class Roo.bootstrap.MessageBar
21918  * @extends Roo.bootstrap.Component
21919  * Bootstrap MessageBar class
21920  * @cfg {String} html contents of the MessageBar
21921  * @cfg {String} weight (info | success | warning | danger) default info
21922  * @cfg {String} beforeClass insert the bar before the given class
21923  * @cfg {Boolean} closable (true | false) default false
21924  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21925  * 
21926  * @constructor
21927  * Create a new Element
21928  * @param {Object} config The config object
21929  */
21930
21931 Roo.bootstrap.MessageBar = function(config){
21932     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21933 };
21934
21935 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21936     
21937     html: '',
21938     weight: 'info',
21939     closable: false,
21940     fixed: false,
21941     beforeClass: 'bootstrap-sticky-wrap',
21942     
21943     getAutoCreate : function(){
21944         
21945         var cfg = {
21946             tag: 'div',
21947             cls: 'alert alert-dismissable alert-' + this.weight,
21948             cn: [
21949                 {
21950                     tag: 'span',
21951                     cls: 'message',
21952                     html: this.html || ''
21953                 }
21954             ]
21955         }
21956         
21957         if(this.fixed){
21958             cfg.cls += ' alert-messages-fixed';
21959         }
21960         
21961         if(this.closable){
21962             cfg.cn.push({
21963                 tag: 'button',
21964                 cls: 'close',
21965                 html: 'x'
21966             });
21967         }
21968         
21969         return cfg;
21970     },
21971     
21972     onRender : function(ct, position)
21973     {
21974         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21975         
21976         if(!this.el){
21977             var cfg = Roo.apply({},  this.getAutoCreate());
21978             cfg.id = Roo.id();
21979             
21980             if (this.cls) {
21981                 cfg.cls += ' ' + this.cls;
21982             }
21983             if (this.style) {
21984                 cfg.style = this.style;
21985             }
21986             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21987             
21988             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21989         }
21990         
21991         this.el.select('>button.close').on('click', this.hide, this);
21992         
21993     },
21994     
21995     show : function()
21996     {
21997         if (!this.rendered) {
21998             this.render();
21999         }
22000         
22001         this.el.show();
22002         
22003         this.fireEvent('show', this);
22004         
22005     },
22006     
22007     hide : function()
22008     {
22009         if (!this.rendered) {
22010             this.render();
22011         }
22012         
22013         this.el.hide();
22014         
22015         this.fireEvent('hide', this);
22016     },
22017     
22018     update : function()
22019     {
22020 //        var e = this.el.dom.firstChild;
22021 //        
22022 //        if(this.closable){
22023 //            e = e.nextSibling;
22024 //        }
22025 //        
22026 //        e.data = this.html || '';
22027
22028         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22029     }
22030    
22031 });
22032
22033  
22034
22035      /*
22036  * - LGPL
22037  *
22038  * Graph
22039  * 
22040  */
22041
22042
22043 /**
22044  * @class Roo.bootstrap.Graph
22045  * @extends Roo.bootstrap.Component
22046  * Bootstrap Graph class
22047 > Prameters
22048  -sm {number} sm 4
22049  -md {number} md 5
22050  @cfg {String} graphtype  bar | vbar | pie
22051  @cfg {number} g_x coodinator | centre x (pie)
22052  @cfg {number} g_y coodinator | centre y (pie)
22053  @cfg {number} g_r radius (pie)
22054  @cfg {number} g_height height of the chart (respected by all elements in the set)
22055  @cfg {number} g_width width of the chart (respected by all elements in the set)
22056  @cfg {Object} title The title of the chart
22057     
22058  -{Array}  values
22059  -opts (object) options for the chart 
22060      o {
22061      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22062      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22063      o vgutter (number)
22064      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22065      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22066      o to
22067      o stretch (boolean)
22068      o }
22069  -opts (object) options for the pie
22070      o{
22071      o cut
22072      o startAngle (number)
22073      o endAngle (number)
22074      } 
22075  *
22076  * @constructor
22077  * Create a new Input
22078  * @param {Object} config The config object
22079  */
22080
22081 Roo.bootstrap.Graph = function(config){
22082     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22083     
22084     this.addEvents({
22085         // img events
22086         /**
22087          * @event click
22088          * The img click event for the img.
22089          * @param {Roo.EventObject} e
22090          */
22091         "click" : true
22092     });
22093 };
22094
22095 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22096     
22097     sm: 4,
22098     md: 5,
22099     graphtype: 'bar',
22100     g_height: 250,
22101     g_width: 400,
22102     g_x: 50,
22103     g_y: 50,
22104     g_r: 30,
22105     opts:{
22106         //g_colors: this.colors,
22107         g_type: 'soft',
22108         g_gutter: '20%'
22109
22110     },
22111     title : false,
22112
22113     getAutoCreate : function(){
22114         
22115         var cfg = {
22116             tag: 'div',
22117             html : null
22118         }
22119         
22120         
22121         return  cfg;
22122     },
22123
22124     onRender : function(ct,position){
22125         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22126         this.raphael = Raphael(this.el.dom);
22127         
22128                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22129                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22130                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22131                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22132                 /*
22133                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22134                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22135                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22136                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22137                 
22138                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22139                 r.barchart(330, 10, 300, 220, data1);
22140                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22141                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22142                 */
22143                 
22144                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22145                 // r.barchart(30, 30, 560, 250,  xdata, {
22146                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22147                 //     axis : "0 0 1 1",
22148                 //     axisxlabels :  xdata
22149                 //     //yvalues : cols,
22150                    
22151                 // });
22152 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22153 //        
22154 //        this.load(null,xdata,{
22155 //                axis : "0 0 1 1",
22156 //                axisxlabels :  xdata
22157 //                });
22158
22159     },
22160
22161     load : function(graphtype,xdata,opts){
22162         this.raphael.clear();
22163         if(!graphtype) {
22164             graphtype = this.graphtype;
22165         }
22166         if(!opts){
22167             opts = this.opts;
22168         }
22169         var r = this.raphael,
22170             fin = function () {
22171                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22172             },
22173             fout = function () {
22174                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22175             },
22176             pfin = function() {
22177                 this.sector.stop();
22178                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22179
22180                 if (this.label) {
22181                     this.label[0].stop();
22182                     this.label[0].attr({ r: 7.5 });
22183                     this.label[1].attr({ "font-weight": 800 });
22184                 }
22185             },
22186             pfout = function() {
22187                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22188
22189                 if (this.label) {
22190                     this.label[0].animate({ r: 5 }, 500, "bounce");
22191                     this.label[1].attr({ "font-weight": 400 });
22192                 }
22193             };
22194
22195         switch(graphtype){
22196             case 'bar':
22197                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22198                 break;
22199             case 'hbar':
22200                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22201                 break;
22202             case 'pie':
22203 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22204 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22205 //            
22206                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22207                 
22208                 break;
22209
22210         }
22211         
22212         if(this.title){
22213             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22214         }
22215         
22216     },
22217     
22218     setTitle: function(o)
22219     {
22220         this.title = o;
22221     },
22222     
22223     initEvents: function() {
22224         
22225         if(!this.href){
22226             this.el.on('click', this.onClick, this);
22227         }
22228     },
22229     
22230     onClick : function(e)
22231     {
22232         Roo.log('img onclick');
22233         this.fireEvent('click', this, e);
22234     }
22235    
22236 });
22237
22238  
22239 /*
22240  * - LGPL
22241  *
22242  * numberBox
22243  * 
22244  */
22245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22246
22247 /**
22248  * @class Roo.bootstrap.dash.NumberBox
22249  * @extends Roo.bootstrap.Component
22250  * Bootstrap NumberBox class
22251  * @cfg {String} headline Box headline
22252  * @cfg {String} content Box content
22253  * @cfg {String} icon Box icon
22254  * @cfg {String} footer Footer text
22255  * @cfg {String} fhref Footer href
22256  * 
22257  * @constructor
22258  * Create a new NumberBox
22259  * @param {Object} config The config object
22260  */
22261
22262
22263 Roo.bootstrap.dash.NumberBox = function(config){
22264     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22265     
22266 };
22267
22268 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22269     
22270     headline : '',
22271     content : '',
22272     icon : '',
22273     footer : '',
22274     fhref : '',
22275     ficon : '',
22276     
22277     getAutoCreate : function(){
22278         
22279         var cfg = {
22280             tag : 'div',
22281             cls : 'small-box ',
22282             cn : [
22283                 {
22284                     tag : 'div',
22285                     cls : 'inner',
22286                     cn :[
22287                         {
22288                             tag : 'h3',
22289                             cls : 'roo-headline',
22290                             html : this.headline
22291                         },
22292                         {
22293                             tag : 'p',
22294                             cls : 'roo-content',
22295                             html : this.content
22296                         }
22297                     ]
22298                 }
22299             ]
22300         }
22301         
22302         if(this.icon){
22303             cfg.cn.push({
22304                 tag : 'div',
22305                 cls : 'icon',
22306                 cn :[
22307                     {
22308                         tag : 'i',
22309                         cls : 'ion ' + this.icon
22310                     }
22311                 ]
22312             });
22313         }
22314         
22315         if(this.footer){
22316             var footer = {
22317                 tag : 'a',
22318                 cls : 'small-box-footer',
22319                 href : this.fhref || '#',
22320                 html : this.footer
22321             };
22322             
22323             cfg.cn.push(footer);
22324             
22325         }
22326         
22327         return  cfg;
22328     },
22329
22330     onRender : function(ct,position){
22331         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22332
22333
22334        
22335                 
22336     },
22337
22338     setHeadline: function (value)
22339     {
22340         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22341     },
22342     
22343     setFooter: function (value, href)
22344     {
22345         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22346         
22347         if(href){
22348             this.el.select('a.small-box-footer',true).first().attr('href', href);
22349         }
22350         
22351     },
22352
22353     setContent: function (value)
22354     {
22355         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22356     },
22357
22358     initEvents: function() 
22359     {   
22360         
22361     }
22362     
22363 });
22364
22365  
22366 /*
22367  * - LGPL
22368  *
22369  * TabBox
22370  * 
22371  */
22372 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22373
22374 /**
22375  * @class Roo.bootstrap.dash.TabBox
22376  * @extends Roo.bootstrap.Component
22377  * Bootstrap TabBox class
22378  * @cfg {String} title Title of the TabBox
22379  * @cfg {String} icon Icon of the TabBox
22380  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22381  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22382  * 
22383  * @constructor
22384  * Create a new TabBox
22385  * @param {Object} config The config object
22386  */
22387
22388
22389 Roo.bootstrap.dash.TabBox = function(config){
22390     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22391     this.addEvents({
22392         // raw events
22393         /**
22394          * @event addpane
22395          * When a pane is added
22396          * @param {Roo.bootstrap.dash.TabPane} pane
22397          */
22398         "addpane" : true,
22399         /**
22400          * @event activatepane
22401          * When a pane is activated
22402          * @param {Roo.bootstrap.dash.TabPane} pane
22403          */
22404         "activatepane" : true
22405         
22406          
22407     });
22408     
22409     this.panes = [];
22410 };
22411
22412 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22413
22414     title : '',
22415     icon : false,
22416     showtabs : true,
22417     tabScrollable : false,
22418     
22419     getChildContainer : function()
22420     {
22421         return this.el.select('.tab-content', true).first();
22422     },
22423     
22424     getAutoCreate : function(){
22425         
22426         var header = {
22427             tag: 'li',
22428             cls: 'pull-left header',
22429             html: this.title,
22430             cn : []
22431         };
22432         
22433         if(this.icon){
22434             header.cn.push({
22435                 tag: 'i',
22436                 cls: 'fa ' + this.icon
22437             });
22438         }
22439         
22440         var h = {
22441             tag: 'ul',
22442             cls: 'nav nav-tabs pull-right',
22443             cn: [
22444                 header
22445             ]
22446         };
22447         
22448         if(this.tabScrollable){
22449             h = {
22450                 tag: 'div',
22451                 cls: 'tab-header',
22452                 cn: [
22453                     {
22454                         tag: 'ul',
22455                         cls: 'nav nav-tabs pull-right',
22456                         cn: [
22457                             header
22458                         ]
22459                     }
22460                 ]
22461             }
22462         }
22463         
22464         var cfg = {
22465             tag: 'div',
22466             cls: 'nav-tabs-custom',
22467             cn: [
22468                 h,
22469                 {
22470                     tag: 'div',
22471                     cls: 'tab-content no-padding',
22472                     cn: []
22473                 }
22474             ]
22475         }
22476
22477         return  cfg;
22478     },
22479     initEvents : function()
22480     {
22481         //Roo.log('add add pane handler');
22482         this.on('addpane', this.onAddPane, this);
22483     },
22484      /**
22485      * Updates the box title
22486      * @param {String} html to set the title to.
22487      */
22488     setTitle : function(value)
22489     {
22490         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22491     },
22492     onAddPane : function(pane)
22493     {
22494         this.panes.push(pane);
22495         //Roo.log('addpane');
22496         //Roo.log(pane);
22497         // tabs are rendere left to right..
22498         if(!this.showtabs){
22499             return;
22500         }
22501         
22502         var ctr = this.el.select('.nav-tabs', true).first();
22503          
22504          
22505         var existing = ctr.select('.nav-tab',true);
22506         var qty = existing.getCount();;
22507         
22508         
22509         var tab = ctr.createChild({
22510             tag : 'li',
22511             cls : 'nav-tab' + (qty ? '' : ' active'),
22512             cn : [
22513                 {
22514                     tag : 'a',
22515                     href:'#',
22516                     html : pane.title
22517                 }
22518             ]
22519         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22520         pane.tab = tab;
22521         
22522         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22523         if (!qty) {
22524             pane.el.addClass('active');
22525         }
22526         
22527                 
22528     },
22529     onTabClick : function(ev,un,ob,pane)
22530     {
22531         //Roo.log('tab - prev default');
22532         ev.preventDefault();
22533         
22534         
22535         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22536         pane.tab.addClass('active');
22537         //Roo.log(pane.title);
22538         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22539         // technically we should have a deactivate event.. but maybe add later.
22540         // and it should not de-activate the selected tab...
22541         this.fireEvent('activatepane', pane);
22542         pane.el.addClass('active');
22543         pane.fireEvent('activate');
22544         
22545         
22546     },
22547     
22548     getActivePane : function()
22549     {
22550         var r = false;
22551         Roo.each(this.panes, function(p) {
22552             if(p.el.hasClass('active')){
22553                 r = p;
22554                 return false;
22555             }
22556             
22557             return;
22558         });
22559         
22560         return r;
22561     }
22562     
22563     
22564 });
22565
22566  
22567 /*
22568  * - LGPL
22569  *
22570  * Tab pane
22571  * 
22572  */
22573 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22574 /**
22575  * @class Roo.bootstrap.TabPane
22576  * @extends Roo.bootstrap.Component
22577  * Bootstrap TabPane class
22578  * @cfg {Boolean} active (false | true) Default false
22579  * @cfg {String} title title of panel
22580
22581  * 
22582  * @constructor
22583  * Create a new TabPane
22584  * @param {Object} config The config object
22585  */
22586
22587 Roo.bootstrap.dash.TabPane = function(config){
22588     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22589     
22590     this.addEvents({
22591         // raw events
22592         /**
22593          * @event activate
22594          * When a pane is activated
22595          * @param {Roo.bootstrap.dash.TabPane} pane
22596          */
22597         "activate" : true
22598          
22599     });
22600 };
22601
22602 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22603     
22604     active : false,
22605     title : '',
22606     
22607     // the tabBox that this is attached to.
22608     tab : false,
22609      
22610     getAutoCreate : function() 
22611     {
22612         var cfg = {
22613             tag: 'div',
22614             cls: 'tab-pane'
22615         }
22616         
22617         if(this.active){
22618             cfg.cls += ' active';
22619         }
22620         
22621         return cfg;
22622     },
22623     initEvents  : function()
22624     {
22625         //Roo.log('trigger add pane handler');
22626         this.parent().fireEvent('addpane', this)
22627     },
22628     
22629      /**
22630      * Updates the tab title 
22631      * @param {String} html to set the title to.
22632      */
22633     setTitle: function(str)
22634     {
22635         if (!this.tab) {
22636             return;
22637         }
22638         this.title = str;
22639         this.tab.select('a', true).first().dom.innerHTML = str;
22640         
22641     }
22642     
22643     
22644     
22645 });
22646
22647  
22648
22649
22650  /*
22651  * - LGPL
22652  *
22653  * menu
22654  * 
22655  */
22656 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22657
22658 /**
22659  * @class Roo.bootstrap.menu.Menu
22660  * @extends Roo.bootstrap.Component
22661  * Bootstrap Menu class - container for Menu
22662  * @cfg {String} html Text of the menu
22663  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22664  * @cfg {String} icon Font awesome icon
22665  * @cfg {String} pos Menu align to (top | bottom) default bottom
22666  * 
22667  * 
22668  * @constructor
22669  * Create a new Menu
22670  * @param {Object} config The config object
22671  */
22672
22673
22674 Roo.bootstrap.menu.Menu = function(config){
22675     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22676     
22677     this.addEvents({
22678         /**
22679          * @event beforeshow
22680          * Fires before this menu is displayed
22681          * @param {Roo.bootstrap.menu.Menu} this
22682          */
22683         beforeshow : true,
22684         /**
22685          * @event beforehide
22686          * Fires before this menu is hidden
22687          * @param {Roo.bootstrap.menu.Menu} this
22688          */
22689         beforehide : true,
22690         /**
22691          * @event show
22692          * Fires after this menu is displayed
22693          * @param {Roo.bootstrap.menu.Menu} this
22694          */
22695         show : true,
22696         /**
22697          * @event hide
22698          * Fires after this menu is hidden
22699          * @param {Roo.bootstrap.menu.Menu} this
22700          */
22701         hide : true,
22702         /**
22703          * @event click
22704          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22705          * @param {Roo.bootstrap.menu.Menu} this
22706          * @param {Roo.EventObject} e
22707          */
22708         click : true
22709     });
22710     
22711 };
22712
22713 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22714     
22715     submenu : false,
22716     html : '',
22717     weight : 'default',
22718     icon : false,
22719     pos : 'bottom',
22720     
22721     
22722     getChildContainer : function() {
22723         if(this.isSubMenu){
22724             return this.el;
22725         }
22726         
22727         return this.el.select('ul.dropdown-menu', true).first();  
22728     },
22729     
22730     getAutoCreate : function()
22731     {
22732         var text = [
22733             {
22734                 tag : 'span',
22735                 cls : 'roo-menu-text',
22736                 html : this.html
22737             }
22738         ];
22739         
22740         if(this.icon){
22741             text.unshift({
22742                 tag : 'i',
22743                 cls : 'fa ' + this.icon
22744             })
22745         }
22746         
22747         
22748         var cfg = {
22749             tag : 'div',
22750             cls : 'btn-group',
22751             cn : [
22752                 {
22753                     tag : 'button',
22754                     cls : 'dropdown-button btn btn-' + this.weight,
22755                     cn : text
22756                 },
22757                 {
22758                     tag : 'button',
22759                     cls : 'dropdown-toggle btn btn-' + this.weight,
22760                     cn : [
22761                         {
22762                             tag : 'span',
22763                             cls : 'caret'
22764                         }
22765                     ]
22766                 },
22767                 {
22768                     tag : 'ul',
22769                     cls : 'dropdown-menu'
22770                 }
22771             ]
22772             
22773         };
22774         
22775         if(this.pos == 'top'){
22776             cfg.cls += ' dropup';
22777         }
22778         
22779         if(this.isSubMenu){
22780             cfg = {
22781                 tag : 'ul',
22782                 cls : 'dropdown-menu'
22783             }
22784         }
22785         
22786         return cfg;
22787     },
22788     
22789     onRender : function(ct, position)
22790     {
22791         this.isSubMenu = ct.hasClass('dropdown-submenu');
22792         
22793         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22794     },
22795     
22796     initEvents : function() 
22797     {
22798         if(this.isSubMenu){
22799             return;
22800         }
22801         
22802         this.hidden = true;
22803         
22804         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22805         this.triggerEl.on('click', this.onTriggerPress, this);
22806         
22807         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22808         this.buttonEl.on('click', this.onClick, this);
22809         
22810     },
22811     
22812     list : function()
22813     {
22814         if(this.isSubMenu){
22815             return this.el;
22816         }
22817         
22818         return this.el.select('ul.dropdown-menu', true).first();
22819     },
22820     
22821     onClick : function(e)
22822     {
22823         this.fireEvent("click", this, e);
22824     },
22825     
22826     onTriggerPress  : function(e)
22827     {   
22828         if (this.isVisible()) {
22829             this.hide();
22830         } else {
22831             this.show();
22832         }
22833     },
22834     
22835     isVisible : function(){
22836         return !this.hidden;
22837     },
22838     
22839     show : function()
22840     {
22841         this.fireEvent("beforeshow", this);
22842         
22843         this.hidden = false;
22844         this.el.addClass('open');
22845         
22846         Roo.get(document).on("mouseup", this.onMouseUp, this);
22847         
22848         this.fireEvent("show", this);
22849         
22850         
22851     },
22852     
22853     hide : function()
22854     {
22855         this.fireEvent("beforehide", this);
22856         
22857         this.hidden = true;
22858         this.el.removeClass('open');
22859         
22860         Roo.get(document).un("mouseup", this.onMouseUp);
22861         
22862         this.fireEvent("hide", this);
22863     },
22864     
22865     onMouseUp : function()
22866     {
22867         this.hide();
22868     }
22869     
22870 });
22871
22872  
22873  /*
22874  * - LGPL
22875  *
22876  * menu item
22877  * 
22878  */
22879 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22880
22881 /**
22882  * @class Roo.bootstrap.menu.Item
22883  * @extends Roo.bootstrap.Component
22884  * Bootstrap MenuItem class
22885  * @cfg {Boolean} submenu (true | false) default false
22886  * @cfg {String} html text of the item
22887  * @cfg {String} href the link
22888  * @cfg {Boolean} disable (true | false) default false
22889  * @cfg {Boolean} preventDefault (true | false) default true
22890  * @cfg {String} icon Font awesome icon
22891  * @cfg {String} pos Submenu align to (left | right) default right 
22892  * 
22893  * 
22894  * @constructor
22895  * Create a new Item
22896  * @param {Object} config The config object
22897  */
22898
22899
22900 Roo.bootstrap.menu.Item = function(config){
22901     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22902     this.addEvents({
22903         /**
22904          * @event mouseover
22905          * Fires when the mouse is hovering over this menu
22906          * @param {Roo.bootstrap.menu.Item} this
22907          * @param {Roo.EventObject} e
22908          */
22909         mouseover : true,
22910         /**
22911          * @event mouseout
22912          * Fires when the mouse exits this menu
22913          * @param {Roo.bootstrap.menu.Item} this
22914          * @param {Roo.EventObject} e
22915          */
22916         mouseout : true,
22917         // raw events
22918         /**
22919          * @event click
22920          * The raw click event for the entire grid.
22921          * @param {Roo.EventObject} e
22922          */
22923         click : true
22924     });
22925 };
22926
22927 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22928     
22929     submenu : false,
22930     href : '',
22931     html : '',
22932     preventDefault: true,
22933     disable : false,
22934     icon : false,
22935     pos : 'right',
22936     
22937     getAutoCreate : function()
22938     {
22939         var text = [
22940             {
22941                 tag : 'span',
22942                 cls : 'roo-menu-item-text',
22943                 html : this.html
22944             }
22945         ];
22946         
22947         if(this.icon){
22948             text.unshift({
22949                 tag : 'i',
22950                 cls : 'fa ' + this.icon
22951             })
22952         }
22953         
22954         var cfg = {
22955             tag : 'li',
22956             cn : [
22957                 {
22958                     tag : 'a',
22959                     href : this.href || '#',
22960                     cn : text
22961                 }
22962             ]
22963         };
22964         
22965         if(this.disable){
22966             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22967         }
22968         
22969         if(this.submenu){
22970             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22971             
22972             if(this.pos == 'left'){
22973                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22974             }
22975         }
22976         
22977         return cfg;
22978     },
22979     
22980     initEvents : function() 
22981     {
22982         this.el.on('mouseover', this.onMouseOver, this);
22983         this.el.on('mouseout', this.onMouseOut, this);
22984         
22985         this.el.select('a', true).first().on('click', this.onClick, this);
22986         
22987     },
22988     
22989     onClick : function(e)
22990     {
22991         if(this.preventDefault){
22992             e.preventDefault();
22993         }
22994         
22995         this.fireEvent("click", this, e);
22996     },
22997     
22998     onMouseOver : function(e)
22999     {
23000         if(this.submenu && this.pos == 'left'){
23001             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23002         }
23003         
23004         this.fireEvent("mouseover", this, e);
23005     },
23006     
23007     onMouseOut : function(e)
23008     {
23009         this.fireEvent("mouseout", this, e);
23010     }
23011 });
23012
23013  
23014
23015  /*
23016  * - LGPL
23017  *
23018  * menu separator
23019  * 
23020  */
23021 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23022
23023 /**
23024  * @class Roo.bootstrap.menu.Separator
23025  * @extends Roo.bootstrap.Component
23026  * Bootstrap Separator class
23027  * 
23028  * @constructor
23029  * Create a new Separator
23030  * @param {Object} config The config object
23031  */
23032
23033
23034 Roo.bootstrap.menu.Separator = function(config){
23035     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23036 };
23037
23038 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23039     
23040     getAutoCreate : function(){
23041         var cfg = {
23042             tag : 'li',
23043             cls: 'divider'
23044         };
23045         
23046         return cfg;
23047     }
23048    
23049 });
23050
23051  
23052
23053  /*
23054  * - LGPL
23055  *
23056  * Tooltip
23057  * 
23058  */
23059
23060 /**
23061  * @class Roo.bootstrap.Tooltip
23062  * Bootstrap Tooltip class
23063  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23064  * to determine which dom element triggers the tooltip.
23065  * 
23066  * It needs to add support for additional attributes like tooltip-position
23067  * 
23068  * @constructor
23069  * Create a new Toolti
23070  * @param {Object} config The config object
23071  */
23072
23073 Roo.bootstrap.Tooltip = function(config){
23074     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23075 };
23076
23077 Roo.apply(Roo.bootstrap.Tooltip, {
23078     /**
23079      * @function init initialize tooltip monitoring.
23080      * @static
23081      */
23082     currentEl : false,
23083     currentTip : false,
23084     currentRegion : false,
23085     
23086     //  init : delay?
23087     
23088     init : function()
23089     {
23090         Roo.get(document).on('mouseover', this.enter ,this);
23091         Roo.get(document).on('mouseout', this.leave, this);
23092          
23093         
23094         this.currentTip = new Roo.bootstrap.Tooltip();
23095     },
23096     
23097     enter : function(ev)
23098     {
23099         var dom = ev.getTarget();
23100         
23101         //Roo.log(['enter',dom]);
23102         var el = Roo.fly(dom);
23103         if (this.currentEl) {
23104             //Roo.log(dom);
23105             //Roo.log(this.currentEl);
23106             //Roo.log(this.currentEl.contains(dom));
23107             if (this.currentEl == el) {
23108                 return;
23109             }
23110             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23111                 return;
23112             }
23113
23114         }
23115         
23116         
23117         
23118         if (this.currentTip.el) {
23119             this.currentTip.el.hide(); // force hiding...
23120         }    
23121         //Roo.log(ev);
23122         var bindEl = el;
23123         
23124         // you can not look for children, as if el is the body.. then everythign is the child..
23125         if (!el.attr('tooltip')) { //
23126             if (!el.select("[tooltip]").elements.length) {
23127                 return;
23128             }
23129             // is the mouse over this child...?
23130             bindEl = el.select("[tooltip]").first();
23131             var xy = ev.getXY();
23132             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23133                 //Roo.log("not in region.");
23134                 return;
23135             }
23136             //Roo.log("child element over..");
23137             
23138         }
23139         this.currentEl = bindEl;
23140         this.currentTip.bind(bindEl);
23141         this.currentRegion = Roo.lib.Region.getRegion(dom);
23142         this.currentTip.enter();
23143         
23144     },
23145     leave : function(ev)
23146     {
23147         var dom = ev.getTarget();
23148         //Roo.log(['leave',dom]);
23149         if (!this.currentEl) {
23150             return;
23151         }
23152         
23153         
23154         if (dom != this.currentEl.dom) {
23155             return;
23156         }
23157         var xy = ev.getXY();
23158         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23159             return;
23160         }
23161         // only activate leave if mouse cursor is outside... bounding box..
23162         
23163         
23164         
23165         
23166         if (this.currentTip) {
23167             this.currentTip.leave();
23168         }
23169         //Roo.log('clear currentEl');
23170         this.currentEl = false;
23171         
23172         
23173     },
23174     alignment : {
23175         'left' : ['r-l', [-2,0], 'right'],
23176         'right' : ['l-r', [2,0], 'left'],
23177         'bottom' : ['t-b', [0,2], 'top'],
23178         'top' : [ 'b-t', [0,-2], 'bottom']
23179     }
23180     
23181 });
23182
23183
23184 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23185     
23186     
23187     bindEl : false,
23188     
23189     delay : null, // can be { show : 300 , hide: 500}
23190     
23191     timeout : null,
23192     
23193     hoverState : null, //???
23194     
23195     placement : 'bottom', 
23196     
23197     getAutoCreate : function(){
23198     
23199         var cfg = {
23200            cls : 'tooltip',
23201            role : 'tooltip',
23202            cn : [
23203                 {
23204                     cls : 'tooltip-arrow'
23205                 },
23206                 {
23207                     cls : 'tooltip-inner'
23208                 }
23209            ]
23210         };
23211         
23212         return cfg;
23213     },
23214     bind : function(el)
23215     {
23216         this.bindEl = el;
23217     },
23218       
23219     
23220     enter : function () {
23221        
23222         if (this.timeout != null) {
23223             clearTimeout(this.timeout);
23224         }
23225         
23226         this.hoverState = 'in';
23227          //Roo.log("enter - show");
23228         if (!this.delay || !this.delay.show) {
23229             this.show();
23230             return;
23231         }
23232         var _t = this;
23233         this.timeout = setTimeout(function () {
23234             if (_t.hoverState == 'in') {
23235                 _t.show();
23236             }
23237         }, this.delay.show);
23238     },
23239     leave : function()
23240     {
23241         clearTimeout(this.timeout);
23242     
23243         this.hoverState = 'out';
23244          if (!this.delay || !this.delay.hide) {
23245             this.hide();
23246             return;
23247         }
23248        
23249         var _t = this;
23250         this.timeout = setTimeout(function () {
23251             //Roo.log("leave - timeout");
23252             
23253             if (_t.hoverState == 'out') {
23254                 _t.hide();
23255                 Roo.bootstrap.Tooltip.currentEl = false;
23256             }
23257         }, delay);
23258     },
23259     
23260     show : function ()
23261     {
23262         if (!this.el) {
23263             this.render(document.body);
23264         }
23265         // set content.
23266         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23267         
23268         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23269         
23270         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23271         
23272         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23273         
23274         var placement = typeof this.placement == 'function' ?
23275             this.placement.call(this, this.el, on_el) :
23276             this.placement;
23277             
23278         var autoToken = /\s?auto?\s?/i;
23279         var autoPlace = autoToken.test(placement);
23280         if (autoPlace) {
23281             placement = placement.replace(autoToken, '') || 'top';
23282         }
23283         
23284         //this.el.detach()
23285         //this.el.setXY([0,0]);
23286         this.el.show();
23287         //this.el.dom.style.display='block';
23288         this.el.addClass(placement);
23289         
23290         //this.el.appendTo(on_el);
23291         
23292         var p = this.getPosition();
23293         var box = this.el.getBox();
23294         
23295         if (autoPlace) {
23296             // fixme..
23297         }
23298         var align = Roo.bootstrap.Tooltip.alignment[placement];
23299         this.el.alignTo(this.bindEl, align[0],align[1]);
23300         //var arrow = this.el.select('.arrow',true).first();
23301         //arrow.set(align[2], 
23302         
23303         this.el.addClass('in fade');
23304         this.hoverState = null;
23305         
23306         if (this.el.hasClass('fade')) {
23307             // fade it?
23308         }
23309         
23310     },
23311     hide : function()
23312     {
23313          
23314         if (!this.el) {
23315             return;
23316         }
23317         //this.el.setXY([0,0]);
23318         this.el.removeClass('in');
23319         //this.el.hide();
23320         
23321     }
23322     
23323 });
23324  
23325
23326  /*
23327  * - LGPL
23328  *
23329  * Location Picker
23330  * 
23331  */
23332
23333 /**
23334  * @class Roo.bootstrap.LocationPicker
23335  * @extends Roo.bootstrap.Component
23336  * Bootstrap LocationPicker class
23337  * @cfg {Number} latitude Position when init default 0
23338  * @cfg {Number} longitude Position when init default 0
23339  * @cfg {Number} zoom default 15
23340  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23341  * @cfg {Boolean} mapTypeControl default false
23342  * @cfg {Boolean} disableDoubleClickZoom default false
23343  * @cfg {Boolean} scrollwheel default true
23344  * @cfg {Boolean} streetViewControl default false
23345  * @cfg {Number} radius default 0
23346  * @cfg {String} locationName
23347  * @cfg {Boolean} draggable default true
23348  * @cfg {Boolean} enableAutocomplete default false
23349  * @cfg {Boolean} enableReverseGeocode default true
23350  * @cfg {String} markerTitle
23351  * 
23352  * @constructor
23353  * Create a new LocationPicker
23354  * @param {Object} config The config object
23355  */
23356
23357
23358 Roo.bootstrap.LocationPicker = function(config){
23359     
23360     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23361     
23362     this.addEvents({
23363         /**
23364          * @event initial
23365          * Fires when the picker initialized.
23366          * @param {Roo.bootstrap.LocationPicker} this
23367          * @param {Google Location} location
23368          */
23369         initial : true,
23370         /**
23371          * @event positionchanged
23372          * Fires when the picker position changed.
23373          * @param {Roo.bootstrap.LocationPicker} this
23374          * @param {Google Location} location
23375          */
23376         positionchanged : true,
23377         /**
23378          * @event resize
23379          * Fires when the map resize.
23380          * @param {Roo.bootstrap.LocationPicker} this
23381          */
23382         resize : true,
23383         /**
23384          * @event show
23385          * Fires when the map show.
23386          * @param {Roo.bootstrap.LocationPicker} this
23387          */
23388         show : true,
23389         /**
23390          * @event hide
23391          * Fires when the map hide.
23392          * @param {Roo.bootstrap.LocationPicker} this
23393          */
23394         hide : true,
23395         /**
23396          * @event mapClick
23397          * Fires when click the map.
23398          * @param {Roo.bootstrap.LocationPicker} this
23399          * @param {Map event} e
23400          */
23401         mapClick : true,
23402         /**
23403          * @event mapRightClick
23404          * Fires when right click the map.
23405          * @param {Roo.bootstrap.LocationPicker} this
23406          * @param {Map event} e
23407          */
23408         mapRightClick : true,
23409         /**
23410          * @event markerClick
23411          * Fires when click the marker.
23412          * @param {Roo.bootstrap.LocationPicker} this
23413          * @param {Map event} e
23414          */
23415         markerClick : true,
23416         /**
23417          * @event markerRightClick
23418          * Fires when right click the marker.
23419          * @param {Roo.bootstrap.LocationPicker} this
23420          * @param {Map event} e
23421          */
23422         markerRightClick : true,
23423         /**
23424          * @event OverlayViewDraw
23425          * Fires when OverlayView Draw
23426          * @param {Roo.bootstrap.LocationPicker} this
23427          */
23428         OverlayViewDraw : true,
23429         /**
23430          * @event OverlayViewOnAdd
23431          * Fires when OverlayView Draw
23432          * @param {Roo.bootstrap.LocationPicker} this
23433          */
23434         OverlayViewOnAdd : true,
23435         /**
23436          * @event OverlayViewOnRemove
23437          * Fires when OverlayView Draw
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          */
23440         OverlayViewOnRemove : true,
23441         /**
23442          * @event OverlayViewShow
23443          * Fires when OverlayView Draw
23444          * @param {Roo.bootstrap.LocationPicker} this
23445          * @param {Pixel} cpx
23446          */
23447         OverlayViewShow : true,
23448         /**
23449          * @event OverlayViewHide
23450          * Fires when OverlayView Draw
23451          * @param {Roo.bootstrap.LocationPicker} this
23452          */
23453         OverlayViewHide : true
23454     });
23455         
23456 };
23457
23458 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23459     
23460     gMapContext: false,
23461     
23462     latitude: 0,
23463     longitude: 0,
23464     zoom: 15,
23465     mapTypeId: false,
23466     mapTypeControl: false,
23467     disableDoubleClickZoom: false,
23468     scrollwheel: true,
23469     streetViewControl: false,
23470     radius: 0,
23471     locationName: '',
23472     draggable: true,
23473     enableAutocomplete: false,
23474     enableReverseGeocode: true,
23475     markerTitle: '',
23476     
23477     getAutoCreate: function()
23478     {
23479
23480         var cfg = {
23481             tag: 'div',
23482             cls: 'roo-location-picker'
23483         };
23484         
23485         return cfg
23486     },
23487     
23488     initEvents: function(ct, position)
23489     {       
23490         if(!this.el.getWidth() || this.isApplied()){
23491             return;
23492         }
23493         
23494         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23495         
23496         this.initial();
23497     },
23498     
23499     initial: function()
23500     {
23501         if(!this.mapTypeId){
23502             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23503         }
23504         
23505         this.gMapContext = this.GMapContext();
23506         
23507         this.initOverlayView();
23508         
23509         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23510         
23511         var _this = this;
23512                 
23513         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23514             _this.setPosition(_this.gMapContext.marker.position);
23515         });
23516         
23517         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23518             _this.fireEvent('mapClick', this, event);
23519             
23520         });
23521
23522         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23523             _this.fireEvent('mapRightClick', this, event);
23524             
23525         });
23526         
23527         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23528             _this.fireEvent('markerClick', this, event);
23529             
23530         });
23531
23532         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23533             _this.fireEvent('markerRightClick', this, event);
23534             
23535         });
23536         
23537         this.setPosition(this.gMapContext.location);
23538         
23539         this.fireEvent('initial', this, this.gMapContext.location);
23540     },
23541     
23542     initOverlayView: function()
23543     {
23544         var _this = this;
23545         
23546         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23547             
23548             draw: function()
23549             {
23550                 _this.fireEvent('OverlayViewDraw', _this);
23551             },
23552             
23553             onAdd: function()
23554             {
23555                 _this.fireEvent('OverlayViewOnAdd', _this);
23556             },
23557             
23558             onRemove: function()
23559             {
23560                 _this.fireEvent('OverlayViewOnRemove', _this);
23561             },
23562             
23563             show: function(cpx)
23564             {
23565                 _this.fireEvent('OverlayViewShow', _this, cpx);
23566             },
23567             
23568             hide: function()
23569             {
23570                 _this.fireEvent('OverlayViewHide', _this);
23571             }
23572             
23573         });
23574     },
23575     
23576     fromLatLngToContainerPixel: function(event)
23577     {
23578         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23579     },
23580     
23581     isApplied: function() 
23582     {
23583         return this.getGmapContext() == false ? false : true;
23584     },
23585     
23586     getGmapContext: function() 
23587     {
23588         return this.gMapContext
23589     },
23590     
23591     GMapContext: function() 
23592     {
23593         var position = new google.maps.LatLng(this.latitude, this.longitude);
23594         
23595         var _map = new google.maps.Map(this.el.dom, {
23596             center: position,
23597             zoom: this.zoom,
23598             mapTypeId: this.mapTypeId,
23599             mapTypeControl: this.mapTypeControl,
23600             disableDoubleClickZoom: this.disableDoubleClickZoom,
23601             scrollwheel: this.scrollwheel,
23602             streetViewControl: this.streetViewControl,
23603             locationName: this.locationName,
23604             draggable: this.draggable,
23605             enableAutocomplete: this.enableAutocomplete,
23606             enableReverseGeocode: this.enableReverseGeocode
23607         });
23608         
23609         var _marker = new google.maps.Marker({
23610             position: position,
23611             map: _map,
23612             title: this.markerTitle,
23613             draggable: this.draggable
23614         });
23615         
23616         return {
23617             map: _map,
23618             marker: _marker,
23619             circle: null,
23620             location: position,
23621             radius: this.radius,
23622             locationName: this.locationName,
23623             addressComponents: {
23624                 formatted_address: null,
23625                 addressLine1: null,
23626                 addressLine2: null,
23627                 streetName: null,
23628                 streetNumber: null,
23629                 city: null,
23630                 district: null,
23631                 state: null,
23632                 stateOrProvince: null
23633             },
23634             settings: this,
23635             domContainer: this.el.dom,
23636             geodecoder: new google.maps.Geocoder()
23637         };
23638     },
23639     
23640     drawCircle: function(center, radius, options) 
23641     {
23642         if (this.gMapContext.circle != null) {
23643             this.gMapContext.circle.setMap(null);
23644         }
23645         if (radius > 0) {
23646             radius *= 1;
23647             options = Roo.apply({}, options, {
23648                 strokeColor: "#0000FF",
23649                 strokeOpacity: .35,
23650                 strokeWeight: 2,
23651                 fillColor: "#0000FF",
23652                 fillOpacity: .2
23653             });
23654             
23655             options.map = this.gMapContext.map;
23656             options.radius = radius;
23657             options.center = center;
23658             this.gMapContext.circle = new google.maps.Circle(options);
23659             return this.gMapContext.circle;
23660         }
23661         
23662         return null;
23663     },
23664     
23665     setPosition: function(location) 
23666     {
23667         this.gMapContext.location = location;
23668         this.gMapContext.marker.setPosition(location);
23669         this.gMapContext.map.panTo(location);
23670         this.drawCircle(location, this.gMapContext.radius, {});
23671         
23672         var _this = this;
23673         
23674         if (this.gMapContext.settings.enableReverseGeocode) {
23675             this.gMapContext.geodecoder.geocode({
23676                 latLng: this.gMapContext.location
23677             }, function(results, status) {
23678                 
23679                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23680                     _this.gMapContext.locationName = results[0].formatted_address;
23681                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23682                     
23683                     _this.fireEvent('positionchanged', this, location);
23684                 }
23685             });
23686             
23687             return;
23688         }
23689         
23690         this.fireEvent('positionchanged', this, location);
23691     },
23692     
23693     resize: function()
23694     {
23695         google.maps.event.trigger(this.gMapContext.map, "resize");
23696         
23697         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23698         
23699         this.fireEvent('resize', this);
23700     },
23701     
23702     setPositionByLatLng: function(latitude, longitude)
23703     {
23704         this.setPosition(new google.maps.LatLng(latitude, longitude));
23705     },
23706     
23707     getCurrentPosition: function() 
23708     {
23709         return {
23710             latitude: this.gMapContext.location.lat(),
23711             longitude: this.gMapContext.location.lng()
23712         };
23713     },
23714     
23715     getAddressName: function() 
23716     {
23717         return this.gMapContext.locationName;
23718     },
23719     
23720     getAddressComponents: function() 
23721     {
23722         return this.gMapContext.addressComponents;
23723     },
23724     
23725     address_component_from_google_geocode: function(address_components) 
23726     {
23727         var result = {};
23728         
23729         for (var i = 0; i < address_components.length; i++) {
23730             var component = address_components[i];
23731             if (component.types.indexOf("postal_code") >= 0) {
23732                 result.postalCode = component.short_name;
23733             } else if (component.types.indexOf("street_number") >= 0) {
23734                 result.streetNumber = component.short_name;
23735             } else if (component.types.indexOf("route") >= 0) {
23736                 result.streetName = component.short_name;
23737             } else if (component.types.indexOf("neighborhood") >= 0) {
23738                 result.city = component.short_name;
23739             } else if (component.types.indexOf("locality") >= 0) {
23740                 result.city = component.short_name;
23741             } else if (component.types.indexOf("sublocality") >= 0) {
23742                 result.district = component.short_name;
23743             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23744                 result.stateOrProvince = component.short_name;
23745             } else if (component.types.indexOf("country") >= 0) {
23746                 result.country = component.short_name;
23747             }
23748         }
23749         
23750         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23751         result.addressLine2 = "";
23752         return result;
23753     },
23754     
23755     setZoomLevel: function(zoom)
23756     {
23757         this.gMapContext.map.setZoom(zoom);
23758     },
23759     
23760     show: function()
23761     {
23762         if(!this.el){
23763             return;
23764         }
23765         
23766         this.el.show();
23767         
23768         this.resize();
23769         
23770         this.fireEvent('show', this);
23771     },
23772     
23773     hide: function()
23774     {
23775         if(!this.el){
23776             return;
23777         }
23778         
23779         this.el.hide();
23780         
23781         this.fireEvent('hide', this);
23782     }
23783     
23784 });
23785
23786 Roo.apply(Roo.bootstrap.LocationPicker, {
23787     
23788     OverlayView : function(map, options)
23789     {
23790         options = options || {};
23791         
23792         this.setMap(map);
23793     }
23794     
23795     
23796 });/*
23797  * - LGPL
23798  *
23799  * Alert
23800  * 
23801  */
23802
23803 /**
23804  * @class Roo.bootstrap.Alert
23805  * @extends Roo.bootstrap.Component
23806  * Bootstrap Alert class
23807  * @cfg {String} title The title of alert
23808  * @cfg {String} html The content of alert
23809  * @cfg {String} weight (  success | info | warning | danger )
23810  * @cfg {String} faicon font-awesomeicon
23811  * 
23812  * @constructor
23813  * Create a new alert
23814  * @param {Object} config The config object
23815  */
23816
23817
23818 Roo.bootstrap.Alert = function(config){
23819     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23820     
23821 };
23822
23823 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23824     
23825     title: '',
23826     html: '',
23827     weight: false,
23828     faicon: false,
23829     
23830     getAutoCreate : function()
23831     {
23832         
23833         var cfg = {
23834             tag : 'div',
23835             cls : 'alert',
23836             cn : [
23837                 {
23838                     tag : 'i',
23839                     cls : 'roo-alert-icon'
23840                     
23841                 },
23842                 {
23843                     tag : 'b',
23844                     cls : 'roo-alert-title',
23845                     html : this.title
23846                 },
23847                 {
23848                     tag : 'span',
23849                     cls : 'roo-alert-text',
23850                     html : this.html
23851                 }
23852             ]
23853         };
23854         
23855         if(this.faicon){
23856             cfg.cn[0].cls += ' fa ' + this.faicon;
23857         }
23858         
23859         if(this.weight){
23860             cfg.cls += ' alert-' + this.weight;
23861         }
23862         
23863         return cfg;
23864     },
23865     
23866     initEvents: function() 
23867     {
23868         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23869     },
23870     
23871     setTitle : function(str)
23872     {
23873         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23874     },
23875     
23876     setText : function(str)
23877     {
23878         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23879     },
23880     
23881     setWeight : function(weight)
23882     {
23883         if(this.weight){
23884             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23885         }
23886         
23887         this.weight = weight;
23888         
23889         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23890     },
23891     
23892     setIcon : function(icon)
23893     {
23894         if(this.faicon){
23895             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23896         }
23897         
23898         this.faicon = icon
23899         
23900         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23901     },
23902     
23903     hide: function() 
23904     {
23905         this.el.hide();   
23906     },
23907     
23908     show: function() 
23909     {  
23910         this.el.show();   
23911     }
23912     
23913 });
23914
23915