Roo/bootstrap/NavItem.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         this.el.removeClass('hidden');
350     },
351     /**
352      * Hide a component - adds 'hidden' class
353      */
354     hide: function()
355     {
356         if (!this.el.hasClass('hidden')) {
357             this.el.addClass('hidden');
358         }
359         
360     }
361 });
362
363  /*
364  * - LGPL
365  *
366  * Body
367  * 
368  */
369
370 /**
371  * @class Roo.bootstrap.Body
372  * @extends Roo.bootstrap.Component
373  * Bootstrap Body class
374  * 
375  * @constructor
376  * Create a new body
377  * @param {Object} config The config object
378  */
379
380 Roo.bootstrap.Body = function(config){
381     Roo.bootstrap.Body.superclass.constructor.call(this, config);
382     this.el = Roo.get(document.body);
383     if (this.cls && this.cls.length) {
384         Roo.get(document.body).addClass(this.cls);
385     }
386 };
387
388 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
389       
390         autoCreate : {
391         cls: 'container'
392     },
393     onRender : function(ct, position)
394     {
395        /* Roo.log("Roo.bootstrap.Body - onRender");
396         if (this.cls && this.cls.length) {
397             Roo.get(document.body).addClass(this.cls);
398         }
399         // style??? xttr???
400         */
401     }
402     
403     
404  
405    
406 });
407
408  /*
409  * - LGPL
410  *
411  * button group
412  * 
413  */
414
415
416 /**
417  * @class Roo.bootstrap.ButtonGroup
418  * @extends Roo.bootstrap.Component
419  * Bootstrap ButtonGroup class
420  * @cfg {String} size lg | sm | xs (default empty normal)
421  * @cfg {String} align vertical | justified  (default none)
422  * @cfg {String} direction up | down (default down)
423  * @cfg {Boolean} toolbar false | true
424  * @cfg {Boolean} btn true | false
425  * 
426  * 
427  * @constructor
428  * Create a new Input
429  * @param {Object} config The config object
430  */
431
432 Roo.bootstrap.ButtonGroup = function(config){
433     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
434 };
435
436 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
437     
438     size: '',
439     align: '',
440     direction: '',
441     toolbar: false,
442     btn: true,
443
444     getAutoCreate : function(){
445         var cfg = {
446             cls: 'btn-group',
447             html : null
448         }
449         
450         cfg.html = this.html || cfg.html;
451         
452         if (this.toolbar) {
453             cfg = {
454                 cls: 'btn-toolbar',
455                 html: null
456             }
457             
458             return cfg;
459         }
460         
461         if (['vertical','justified'].indexOf(this.align)!==-1) {
462             cfg.cls = 'btn-group-' + this.align;
463             
464             if (this.align == 'justified') {
465                 console.log(this.items);
466             }
467         }
468         
469         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
470             cfg.cls += ' btn-group-' + this.size;
471         }
472         
473         if (this.direction == 'up') {
474             cfg.cls += ' dropup' ;
475         }
476         
477         return cfg;
478     }
479    
480 });
481
482  /*
483  * - LGPL
484  *
485  * button
486  * 
487  */
488
489 /**
490  * @class Roo.bootstrap.Button
491  * @extends Roo.bootstrap.Component
492  * Bootstrap Button class
493  * @cfg {String} html The button content
494  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
495  * @cfg {String} size ( lg | sm | xs)
496  * @cfg {String} tag ( a | input | submit)
497  * @cfg {String} href empty or href
498  * @cfg {Boolean} disabled default false;
499  * @cfg {Boolean} isClose default false;
500  * @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)
501  * @cfg {String} badge text for badge
502  * @cfg {String} theme default 
503  * @cfg {Boolean} inverse 
504  * @cfg {Boolean} toggle 
505  * @cfg {String} ontext text for on toggle state
506  * @cfg {String} offtext text for off toggle state
507  * @cfg {Boolean} defaulton 
508  * @cfg {Boolean} preventDefault  default true
509  * @cfg {Boolean} removeClass remove the standard class..
510  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
511  * 
512  * @constructor
513  * Create a new button
514  * @param {Object} config The config object
515  */
516
517
518 Roo.bootstrap.Button = function(config){
519     Roo.bootstrap.Button.superclass.constructor.call(this, config);
520     this.addEvents({
521         // raw events
522         /**
523          * @event click
524          * When a butotn is pressed
525          * @param {Roo.bootstrap.Button} this
526          * @param {Roo.EventObject} e
527          */
528         "click" : true,
529          /**
530          * @event toggle
531          * After the button has been toggles
532          * @param {Roo.EventObject} e
533          * @param {boolean} pressed (also available as button.pressed)
534          */
535         "toggle" : true
536     });
537 };
538
539 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
540     html: false,
541     active: false,
542     weight: '',
543     size: '',
544     tag: 'button',
545     href: '',
546     disabled: false,
547     isClose: false,
548     glyphicon: '',
549     badge: '',
550     theme: 'default',
551     inverse: false,
552     
553     toggle: false,
554     ontext: 'ON',
555     offtext: 'OFF',
556     defaulton: true,
557     preventDefault: true,
558     removeClass: false,
559     name: false,
560     target: false,
561     
562     
563     pressed : null,
564      
565     
566     getAutoCreate : function(){
567         
568         var cfg = {
569             tag : 'button',
570             cls : 'roo-button',
571             html: ''
572         };
573         
574         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
575             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
576             this.tag = 'button';
577         } else {
578             cfg.tag = this.tag;
579         }
580         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
581         
582         if (this.toggle == true) {
583             cfg={
584                 tag: 'div',
585                 cls: 'slider-frame roo-button',
586                 cn: [
587                     {
588                         tag: 'span',
589                         'data-on-text':'ON',
590                         'data-off-text':'OFF',
591                         cls: 'slider-button',
592                         html: this.offtext
593                     }
594                 ]
595             };
596             
597             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
598                 cfg.cls += ' '+this.weight;
599             }
600             
601             return cfg;
602         }
603         
604         if (this.isClose) {
605             cfg.cls += ' close';
606             
607             cfg["aria-hidden"] = true;
608             
609             cfg.html = "&times;";
610             
611             return cfg;
612         }
613         
614          
615         if (this.theme==='default') {
616             cfg.cls = 'btn roo-button';
617             
618             //if (this.parentType != 'Navbar') {
619             this.weight = this.weight.length ?  this.weight : 'default';
620             //}
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 
623                 cfg.cls += ' btn-' + this.weight;
624             }
625         } else if (this.theme==='glow') {
626             
627             cfg.tag = 'a';
628             cfg.cls = 'btn-glow roo-button';
629             
630             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
631                 
632                 cfg.cls += ' ' + this.weight;
633             }
634         }
635    
636         
637         if (this.inverse) {
638             this.cls += ' inverse';
639         }
640         
641         
642         if (this.active) {
643             cfg.cls += ' active';
644         }
645         
646         if (this.disabled) {
647             cfg.disabled = 'disabled';
648         }
649         
650         if (this.items) {
651             Roo.log('changing to ul' );
652             cfg.tag = 'ul';
653             this.glyphicon = 'caret';
654         }
655         
656         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
657          
658         //gsRoo.log(this.parentType);
659         if (this.parentType === 'Navbar' && !this.parent().bar) {
660             Roo.log('changing to li?');
661             
662             cfg.tag = 'li';
663             
664             cfg.cls = '';
665             cfg.cn =  [{
666                 tag : 'a',
667                 cls : 'roo-button',
668                 html : this.html,
669                 href : this.href || '#'
670             }];
671             if (this.menu) {
672                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
673                 cfg.cls += ' dropdown';
674             }   
675             
676             delete cfg.html;
677             
678         }
679         
680        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
681         
682         if (this.glyphicon) {
683             cfg.html = ' ' + cfg.html;
684             
685             cfg.cn = [
686                 {
687                     tag: 'span',
688                     cls: 'glyphicon glyphicon-' + this.glyphicon
689                 }
690             ];
691         }
692         
693         if (this.badge) {
694             cfg.html += ' ';
695             
696             cfg.tag = 'a';
697             
698 //            cfg.cls='btn roo-button';
699             
700             cfg.href=this.href;
701             
702             var value = cfg.html;
703             
704             if(this.glyphicon){
705                 value = {
706                             tag: 'span',
707                             cls: 'glyphicon glyphicon-' + this.glyphicon,
708                             html: this.html
709                         };
710                 
711             }
712             
713             cfg.cn = [
714                 value,
715                 {
716                     tag: 'span',
717                     cls: 'badge',
718                     html: this.badge
719                 }
720             ];
721             
722             cfg.html='';
723         }
724         
725         if (this.menu) {
726             cfg.cls += ' dropdown';
727             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
728         }
729         
730         if (cfg.tag !== 'a' && this.href !== '') {
731             throw "Tag must be a to set href.";
732         } else if (this.href.length > 0) {
733             cfg.href = this.href;
734         }
735         
736         if(this.removeClass){
737             cfg.cls = '';
738         }
739         
740         if(this.target){
741             cfg.target = this.target;
742         }
743         
744         return cfg;
745     },
746     initEvents: function() {
747        // Roo.log('init events?');
748 //        Roo.log(this.el.dom);
749         // add the menu...
750         
751         if (typeof (this.menu) != 'undefined') {
752             this.menu.parentType = this.xtype;
753             this.menu.triggerEl = this.el;
754             this.addxtype(Roo.apply({}, this.menu));
755         }
756
757
758        if (this.el.hasClass('roo-button')) {
759             this.el.on('click', this.onClick, this);
760        } else {
761             this.el.select('.roo-button').on('click', this.onClick, this);
762        }
763        
764        if(this.removeClass){
765            this.el.on('click', this.onClick, this);
766        }
767        
768        this.el.enableDisplayMode();
769         
770     },
771     onClick : function(e)
772     {
773         if (this.disabled) {
774             return;
775         }
776         
777         
778         Roo.log('button on click ');
779         if(this.preventDefault){
780             e.preventDefault();
781         }
782         if (this.pressed === true || this.pressed === false) {
783             this.pressed = !this.pressed;
784             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
785             this.fireEvent('toggle', this, e, this.pressed);
786         }
787         
788         
789         this.fireEvent('click', this, e);
790     },
791     
792     /**
793      * Enables this button
794      */
795     enable : function()
796     {
797         this.disabled = false;
798         this.el.removeClass('disabled');
799     },
800     
801     /**
802      * Disable this button
803      */
804     disable : function()
805     {
806         this.disabled = true;
807         this.el.addClass('disabled');
808     },
809      /**
810      * sets the active state on/off, 
811      * @param {Boolean} state (optional) Force a particular state
812      */
813     setActive : function(v) {
814         
815         this.el[v ? 'addClass' : 'removeClass']('active');
816     },
817      /**
818      * toggles the current active state 
819      */
820     toggleActive : function()
821     {
822        var active = this.el.hasClass('active');
823        this.setActive(!active);
824        
825         
826     },
827     setText : function(str)
828     {
829         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
830     },
831     getText : function()
832     {
833         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
834     },
835     hide: function() {
836        
837      
838         this.el.hide();   
839     },
840     show: function() {
841        
842         this.el.show();   
843     }
844     
845     
846 });
847
848  /*
849  * - LGPL
850  *
851  * column
852  * 
853  */
854
855 /**
856  * @class Roo.bootstrap.Column
857  * @extends Roo.bootstrap.Component
858  * Bootstrap Column class
859  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
860  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
861  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
862  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
863  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
864  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
865  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
866  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
867  *
868  * 
869  * @cfg {Boolean} hidden (true|false) hide the element
870  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
871  * @cfg {String} fa (ban|check|...) font awesome icon
872  * @cfg {Number} fasize (1|2|....) font awsome size
873
874  * @cfg {String} icon (info-sign|check|...) glyphicon name
875
876  * @cfg {String} html content of column.
877  * 
878  * @constructor
879  * Create a new Column
880  * @param {Object} config The config object
881  */
882
883 Roo.bootstrap.Column = function(config){
884     Roo.bootstrap.Column.superclass.constructor.call(this, config);
885 };
886
887 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
888     
889     xs: false,
890     sm: false,
891     md: false,
892     lg: false,
893     xsoff: false,
894     smoff: false,
895     mdoff: false,
896     lgoff: false,
897     html: '',
898     offset: 0,
899     alert: false,
900     fa: false,
901     icon : false,
902     hidden : false,
903     fasize : 1,
904     
905     getAutoCreate : function(){
906         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
907         
908         cfg = {
909             tag: 'div',
910             cls: 'column'
911         };
912         
913         var settings=this;
914         ['xs','sm','md','lg'].map(function(size){
915             //Roo.log( size + ':' + settings[size]);
916             
917             if (settings[size+'off'] !== false) {
918                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
919             }
920             
921             if (settings[size] === false) {
922                 return;
923             }
924             Roo.log(settings[size]);
925             if (!settings[size]) { // 0 = hidden
926                 cfg.cls += ' hidden-' + size;
927                 return;
928             }
929             cfg.cls += ' col-' + size + '-' + settings[size];
930             
931         });
932         
933         if (this.hidden) {
934             cfg.cls += ' hidden';
935         }
936         
937         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
938             cfg.cls +=' alert alert-' + this.alert;
939         }
940         
941         
942         if (this.html.length) {
943             cfg.html = this.html;
944         }
945         if (this.fa) {
946             var fasize = '';
947             if (this.fasize > 1) {
948                 fasize = ' fa-' + this.fasize + 'x';
949             }
950             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
951             
952             
953         }
954         if (this.icon) {
955             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  
964
965  /*
966  * - LGPL
967  *
968  * page container.
969  * 
970  */
971
972
973 /**
974  * @class Roo.bootstrap.Container
975  * @extends Roo.bootstrap.Component
976  * Bootstrap Container class
977  * @cfg {Boolean} jumbotron is it a jumbotron element
978  * @cfg {String} html content of element
979  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
980  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
981  * @cfg {String} header content of header (for panel)
982  * @cfg {String} footer content of footer (for panel)
983  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
984  * @cfg {String} tag (header|aside|section) type of HTML tag.
985  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
986  * @cfg {String} fa (ban|check|...) font awesome icon
987  * @cfg {String} icon (info-sign|check|...) glyphicon name
988  * @cfg {Boolean} hidden (true|false) hide the element
989  * @cfg {Boolean} expandable (true|false) default false
990  * @cfg {String} rheader contet on the right of header
991
992  *     
993  * @constructor
994  * Create a new Container
995  * @param {Object} config The config object
996  */
997
998 Roo.bootstrap.Container = function(config){
999     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1000     
1001     this.addEvents({
1002         // raw events
1003          /**
1004          * @event expand
1005          * After the panel has been expand
1006          * 
1007          * @param {Roo.bootstrap.Container} this
1008          */
1009         "expand" : true,
1010         /**
1011          * @event collapse
1012          * After the panel has been collapsed
1013          * 
1014          * @param {Roo.bootstrap.Container} this
1015          */
1016         "collapse" : true
1017     });
1018 };
1019
1020 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1021     
1022     jumbotron : false,
1023     well: '',
1024     panel : '',
1025     header: '',
1026     footer : '',
1027     sticky: '',
1028     tag : false,
1029     alert : false,
1030     fa: false,
1031     icon : false,
1032     expandable : false,
1033     rheader : '',
1034   
1035      
1036     getChildContainer : function() {
1037         
1038         if(!this.el){
1039             return false;
1040         }
1041         
1042         if (this.panel.length) {
1043             return this.el.select('.panel-body',true).first();
1044         }
1045         
1046         return this.el;
1047     },
1048     
1049     
1050     getAutoCreate : function(){
1051         
1052         var cfg = {
1053             tag : this.tag || 'div',
1054             html : '',
1055             cls : ''
1056         };
1057         if (this.jumbotron) {
1058             cfg.cls = 'jumbotron';
1059         }
1060         
1061         
1062         
1063         // - this is applied by the parent..
1064         //if (this.cls) {
1065         //    cfg.cls = this.cls + '';
1066         //}
1067         
1068         if (this.sticky.length) {
1069             
1070             var bd = Roo.get(document.body);
1071             if (!bd.hasClass('bootstrap-sticky')) {
1072                 bd.addClass('bootstrap-sticky');
1073                 Roo.select('html',true).setStyle('height', '100%');
1074             }
1075              
1076             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1077         }
1078         
1079         
1080         if (this.well.length) {
1081             switch (this.well) {
1082                 case 'lg':
1083                 case 'sm':
1084                     cfg.cls +=' well well-' +this.well;
1085                     break;
1086                 default:
1087                     cfg.cls +=' well';
1088                     break;
1089             }
1090         }
1091         
1092         if (this.hidden) {
1093             cfg.cls += ' hidden';
1094         }
1095         
1096         
1097         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1098             cfg.cls +=' alert alert-' + this.alert;
1099         }
1100         
1101         var body = cfg;
1102         
1103         if (this.panel.length) {
1104             cfg.cls += ' panel panel-' + this.panel;
1105             cfg.cn = [];
1106             if (this.header.length) {
1107                 
1108                 var h = [];
1109                 
1110                 if(this.expandable){
1111                     h.push({
1112                         tag: 'i',
1113                         cls: 'fa fa-minus'
1114                     });
1115                 }
1116                 
1117                 h.push(
1118                     {
1119                         tag: 'span',
1120                         cls : 'panel-title',
1121                         html : this.header
1122                     },
1123                     {
1124                         tag: 'span',
1125                         cls: 'panel-header-right',
1126                         html: this.rheader
1127                     }
1128                 );
1129                 
1130                 cfg.cn.push({
1131                     cls : 'panel-heading',
1132                     cn : h
1133                 });
1134                 
1135             }
1136             
1137             body = false;
1138             cfg.cn.push({
1139                 cls : 'panel-body',
1140                 html : this.html
1141             });
1142             
1143             
1144             if (this.footer.length) {
1145                 cfg.cn.push({
1146                     cls : 'panel-footer',
1147                     html : this.footer
1148                     
1149                 });
1150             }
1151             
1152         }
1153         
1154         if (body) {
1155             body.html = this.html || cfg.html;
1156             // prefix with the icons..
1157             if (this.fa) {
1158                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1159             }
1160             if (this.icon) {
1161                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1162             }
1163             
1164             
1165         }
1166         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1167             cfg.cls =  'container';
1168         }
1169         
1170         return cfg;
1171     },
1172     
1173     initEvents: function() 
1174     {
1175         var toggleEl = this.toggleEl();
1176         
1177         if(!toggleEl){
1178             return;
1179         }
1180         
1181         toggleEl.on('click', this.onToggleClick, this);
1182     },
1183     
1184     onToggleClick : function()
1185     {
1186         var toggleEl = this.toggleEl();
1187         
1188         if(!toggleEl){
1189             return;
1190         }
1191         
1192         if(toggleEl.hasClass('fa-minus')){
1193             this.collapse();
1194             return;
1195         }
1196         
1197         this.expand();
1198         
1199     },
1200     
1201     expand : function()
1202     {
1203         if(this.fireEvent('expand', this)) {
1204             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1205         
1206             var toggleEl = this.toggleEl();
1207
1208             if(!toggleEl){
1209                 return;
1210             }
1211
1212             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1213         }
1214         
1215     },
1216     
1217     collapse : function()
1218     {
1219         if(this.fireEvent('collapse', this)) {
1220             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1221         
1222             var toggleEl = this.toggleEl();
1223
1224             if(!toggleEl){
1225                 return;
1226             }
1227
1228             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1229         }
1230     },
1231     
1232     toggleEl : function()
1233     {
1234         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1235             return;
1236         }
1237         
1238         return this.el.select('.panel-heading .fa',true).first();
1239     },
1240     
1241     titleEl : function()
1242     {
1243         if(!this.el || !this.panel.length || !this.header.length){
1244             return;
1245         }
1246         
1247         return this.el.select('.panel-title',true).first();
1248     },
1249     
1250     setTitle : function(v)
1251     {
1252         var titleEl = this.titleEl();
1253         
1254         if(!titleEl){
1255             return;
1256         }
1257         
1258         titleEl.dom.innerHTML = v;
1259     },
1260     
1261     getTitle : function()
1262     {
1263         
1264         var titleEl = this.titleEl();
1265         
1266         if(!titleEl){
1267             return '';
1268         }
1269         
1270         return titleEl.dom.innerHTML;
1271     },
1272     
1273     setRightTitle : function(v)
1274     {
1275         var t = this.el.select('.panel-header-right',true).first();
1276         
1277         if(!t){
1278             return;
1279         }
1280         
1281         t.dom.innerHTML = v;
1282     }
1283    
1284 });
1285
1286  /*
1287  * - LGPL
1288  *
1289  * image
1290  * 
1291  */
1292
1293
1294 /**
1295  * @class Roo.bootstrap.Img
1296  * @extends Roo.bootstrap.Component
1297  * Bootstrap Img class
1298  * @cfg {Boolean} imgResponsive false | true
1299  * @cfg {String} border rounded | circle | thumbnail
1300  * @cfg {String} src image source
1301  * @cfg {String} alt image alternative text
1302  * @cfg {String} href a tag href
1303  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1304  * 
1305  * @constructor
1306  * Create a new Input
1307  * @param {Object} config The config object
1308  */
1309
1310 Roo.bootstrap.Img = function(config){
1311     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1312     
1313     this.addEvents({
1314         // img events
1315         /**
1316          * @event click
1317          * The img click event for the img.
1318          * @param {Roo.EventObject} e
1319          */
1320         "click" : true
1321     });
1322 };
1323
1324 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1325     
1326     imgResponsive: true,
1327     border: '',
1328     src: '',
1329     href: false,
1330     target: false,
1331
1332     getAutoCreate : function(){
1333         
1334         var cfg = {
1335             tag: 'img',
1336             cls: (this.imgResponsive) ? 'img-responsive' : '',
1337             html : null
1338         }
1339         
1340         cfg.html = this.html || cfg.html;
1341         
1342         cfg.src = this.src || cfg.src;
1343         
1344         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1345             cfg.cls += ' img-' + this.border;
1346         }
1347         
1348         if(this.alt){
1349             cfg.alt = this.alt;
1350         }
1351         
1352         if(this.href){
1353             var a = {
1354                 tag: 'a',
1355                 href: this.href,
1356                 cn: [
1357                     cfg
1358                 ]
1359             }
1360             
1361             if(this.target){
1362                 a.target = this.target;
1363             }
1364             
1365         }
1366         
1367         
1368         return (this.href) ? a : cfg;
1369     },
1370     
1371     initEvents: function() {
1372         
1373         if(!this.href){
1374             this.el.on('click', this.onClick, this);
1375         }
1376     },
1377     
1378     onClick : function(e)
1379     {
1380         Roo.log('img onclick');
1381         this.fireEvent('click', this, e);
1382     }
1383    
1384 });
1385
1386  /*
1387  * - LGPL
1388  *
1389  * image
1390  * 
1391  */
1392
1393
1394 /**
1395  * @class Roo.bootstrap.Link
1396  * @extends Roo.bootstrap.Component
1397  * Bootstrap Link Class
1398  * @cfg {String} alt image alternative text
1399  * @cfg {String} href a tag href
1400  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1401  * @cfg {String} html the content of the link.
1402  * @cfg {String} anchor name for the anchor link
1403
1404  * @cfg {Boolean} preventDefault (true | false) default false
1405
1406  * 
1407  * @constructor
1408  * Create a new Input
1409  * @param {Object} config The config object
1410  */
1411
1412 Roo.bootstrap.Link = function(config){
1413     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1414     
1415     this.addEvents({
1416         // img events
1417         /**
1418          * @event click
1419          * The img click event for the img.
1420          * @param {Roo.EventObject} e
1421          */
1422         "click" : true
1423     });
1424 };
1425
1426 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1427     
1428     href: false,
1429     target: false,
1430     preventDefault: false,
1431     anchor : false,
1432     alt : false,
1433
1434     getAutoCreate : function()
1435     {
1436         
1437         var cfg = {
1438             tag: 'a'
1439         };
1440         // anchor's do not require html/href...
1441         if (this.anchor === false) {
1442             cfg.html = this.html || 'html-missing';
1443             cfg.href = this.href || '#';
1444         } else {
1445             cfg.name = this.anchor;
1446             if (this.html !== false) {
1447                 cfg.html = this.html;
1448             }
1449             if (this.href !== false) {
1450                 cfg.href = this.href;
1451             }
1452         }
1453         
1454         if(this.alt !== false){
1455             cfg.alt = this.alt;
1456         }
1457         
1458         
1459         if(this.target !== false) {
1460             cfg.target = this.target;
1461         }
1462         
1463         return cfg;
1464     },
1465     
1466     initEvents: function() {
1467         
1468         if(!this.href || this.preventDefault){
1469             this.el.on('click', this.onClick, this);
1470         }
1471     },
1472     
1473     onClick : function(e)
1474     {
1475         if(this.preventDefault){
1476             e.preventDefault();
1477         }
1478         //Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * header
1488  * 
1489  */
1490
1491 /**
1492  * @class Roo.bootstrap.Header
1493  * @extends Roo.bootstrap.Component
1494  * Bootstrap Header class
1495  * @cfg {String} html content of header
1496  * @cfg {Number} level (1|2|3|4|5|6) default 1
1497  * 
1498  * @constructor
1499  * Create a new Header
1500  * @param {Object} config The config object
1501  */
1502
1503
1504 Roo.bootstrap.Header  = function(config){
1505     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1506 };
1507
1508 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1509     
1510     //href : false,
1511     html : false,
1512     level : 1,
1513     
1514     
1515     
1516     getAutoCreate : function(){
1517         
1518         
1519         
1520         var cfg = {
1521             tag: 'h' + (1 *this.level),
1522             html: this.html || ''
1523         } ;
1524         
1525         return cfg;
1526     }
1527    
1528 });
1529
1530  
1531
1532  /*
1533  * Based on:
1534  * Ext JS Library 1.1.1
1535  * Copyright(c) 2006-2007, Ext JS, LLC.
1536  *
1537  * Originally Released Under LGPL - original licence link has changed is not relivant.
1538  *
1539  * Fork - LGPL
1540  * <script type="text/javascript">
1541  */
1542  
1543 /**
1544  * @class Roo.bootstrap.MenuMgr
1545  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1546  * @singleton
1547  */
1548 Roo.bootstrap.MenuMgr = function(){
1549    var menus, active, groups = {}, attached = false, lastShow = new Date();
1550
1551    // private - called when first menu is created
1552    function init(){
1553        menus = {};
1554        active = new Roo.util.MixedCollection();
1555        Roo.get(document).addKeyListener(27, function(){
1556            if(active.length > 0){
1557                hideAll();
1558            }
1559        });
1560    }
1561
1562    // private
1563    function hideAll(){
1564        if(active && active.length > 0){
1565            var c = active.clone();
1566            c.each(function(m){
1567                m.hide();
1568            });
1569        }
1570    }
1571
1572    // private
1573    function onHide(m){
1574        active.remove(m);
1575        if(active.length < 1){
1576            Roo.get(document).un("mouseup", onMouseDown);
1577             
1578            attached = false;
1579        }
1580    }
1581
1582    // private
1583    function onShow(m){
1584        var last = active.last();
1585        lastShow = new Date();
1586        active.add(m);
1587        if(!attached){
1588           Roo.get(document).on("mouseup", onMouseDown);
1589            
1590            attached = true;
1591        }
1592        if(m.parentMenu){
1593           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1594           m.parentMenu.activeChild = m;
1595        }else if(last && last.isVisible()){
1596           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1597        }
1598    }
1599
1600    // private
1601    function onBeforeHide(m){
1602        if(m.activeChild){
1603            m.activeChild.hide();
1604        }
1605        if(m.autoHideTimer){
1606            clearTimeout(m.autoHideTimer);
1607            delete m.autoHideTimer;
1608        }
1609    }
1610
1611    // private
1612    function onBeforeShow(m){
1613        var pm = m.parentMenu;
1614        if(!pm && !m.allowOtherMenus){
1615            hideAll();
1616        }else if(pm && pm.activeChild && active != m){
1617            pm.activeChild.hide();
1618        }
1619    }
1620
1621    // private
1622    function onMouseDown(e){
1623         Roo.log("on MouseDown");
1624         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1625            hideAll();
1626         }
1627         
1628         
1629    }
1630
1631    // private
1632    function onBeforeCheck(mi, state){
1633        if(state){
1634            var g = groups[mi.group];
1635            for(var i = 0, l = g.length; i < l; i++){
1636                if(g[i] != mi){
1637                    g[i].setChecked(false);
1638                }
1639            }
1640        }
1641    }
1642
1643    return {
1644
1645        /**
1646         * Hides all menus that are currently visible
1647         */
1648        hideAll : function(){
1649             hideAll();  
1650        },
1651
1652        // private
1653        register : function(menu){
1654            if(!menus){
1655                init();
1656            }
1657            menus[menu.id] = menu;
1658            menu.on("beforehide", onBeforeHide);
1659            menu.on("hide", onHide);
1660            menu.on("beforeshow", onBeforeShow);
1661            menu.on("show", onShow);
1662            var g = menu.group;
1663            if(g && menu.events["checkchange"]){
1664                if(!groups[g]){
1665                    groups[g] = [];
1666                }
1667                groups[g].push(menu);
1668                menu.on("checkchange", onCheck);
1669            }
1670        },
1671
1672         /**
1673          * Returns a {@link Roo.menu.Menu} object
1674          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1675          * be used to generate and return a new Menu instance.
1676          */
1677        get : function(menu){
1678            if(typeof menu == "string"){ // menu id
1679                return menus[menu];
1680            }else if(menu.events){  // menu instance
1681                return menu;
1682            }
1683            /*else if(typeof menu.length == 'number'){ // array of menu items?
1684                return new Roo.bootstrap.Menu({items:menu});
1685            }else{ // otherwise, must be a config
1686                return new Roo.bootstrap.Menu(menu);
1687            }
1688            */
1689            return false;
1690        },
1691
1692        // private
1693        unregister : function(menu){
1694            delete menus[menu.id];
1695            menu.un("beforehide", onBeforeHide);
1696            menu.un("hide", onHide);
1697            menu.un("beforeshow", onBeforeShow);
1698            menu.un("show", onShow);
1699            var g = menu.group;
1700            if(g && menu.events["checkchange"]){
1701                groups[g].remove(menu);
1702                menu.un("checkchange", onCheck);
1703            }
1704        },
1705
1706        // private
1707        registerCheckable : function(menuItem){
1708            var g = menuItem.group;
1709            if(g){
1710                if(!groups[g]){
1711                    groups[g] = [];
1712                }
1713                groups[g].push(menuItem);
1714                menuItem.on("beforecheckchange", onBeforeCheck);
1715            }
1716        },
1717
1718        // private
1719        unregisterCheckable : function(menuItem){
1720            var g = menuItem.group;
1721            if(g){
1722                groups[g].remove(menuItem);
1723                menuItem.un("beforecheckchange", onBeforeCheck);
1724            }
1725        }
1726    };
1727 }();/*
1728  * - LGPL
1729  *
1730  * menu
1731  * 
1732  */
1733
1734 /**
1735  * @class Roo.bootstrap.Menu
1736  * @extends Roo.bootstrap.Component
1737  * Bootstrap Menu class - container for MenuItems
1738  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1739  * 
1740  * @constructor
1741  * Create a new Menu
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Menu = function(config){
1747     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1748     if (this.registerMenu) {
1749         Roo.bootstrap.MenuMgr.register(this);
1750     }
1751     this.addEvents({
1752         /**
1753          * @event beforeshow
1754          * Fires before this menu is displayed
1755          * @param {Roo.menu.Menu} this
1756          */
1757         beforeshow : true,
1758         /**
1759          * @event beforehide
1760          * Fires before this menu is hidden
1761          * @param {Roo.menu.Menu} this
1762          */
1763         beforehide : true,
1764         /**
1765          * @event show
1766          * Fires after this menu is displayed
1767          * @param {Roo.menu.Menu} this
1768          */
1769         show : true,
1770         /**
1771          * @event hide
1772          * Fires after this menu is hidden
1773          * @param {Roo.menu.Menu} this
1774          */
1775         hide : true,
1776         /**
1777          * @event click
1778          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1779          * @param {Roo.menu.Menu} this
1780          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1781          * @param {Roo.EventObject} e
1782          */
1783         click : true,
1784         /**
1785          * @event mouseover
1786          * Fires when the mouse is hovering over this menu
1787          * @param {Roo.menu.Menu} this
1788          * @param {Roo.EventObject} e
1789          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1790          */
1791         mouseover : true,
1792         /**
1793          * @event mouseout
1794          * Fires when the mouse exits this menu
1795          * @param {Roo.menu.Menu} this
1796          * @param {Roo.EventObject} e
1797          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1798          */
1799         mouseout : true,
1800         /**
1801          * @event itemclick
1802          * Fires when a menu item contained in this menu is clicked
1803          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1804          * @param {Roo.EventObject} e
1805          */
1806         itemclick: true
1807     });
1808     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1809 };
1810
1811 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1812     
1813    /// html : false,
1814     //align : '',
1815     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1816     type: false,
1817     /**
1818      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1819      */
1820     registerMenu : true,
1821     
1822     menuItems :false, // stores the menu items..
1823     
1824     hidden:true,
1825     
1826     parentMenu : false,
1827     
1828     getChildContainer : function() {
1829         return this.el;  
1830     },
1831     
1832     getAutoCreate : function(){
1833          
1834         //if (['right'].indexOf(this.align)!==-1) {
1835         //    cfg.cn[1].cls += ' pull-right'
1836         //}
1837         
1838         
1839         var cfg = {
1840             tag : 'ul',
1841             cls : 'dropdown-menu' ,
1842             style : 'z-index:1000'
1843             
1844         }
1845         
1846         if (this.type === 'submenu') {
1847             cfg.cls = 'submenu active';
1848         }
1849         if (this.type === 'treeview') {
1850             cfg.cls = 'treeview-menu';
1851         }
1852         
1853         return cfg;
1854     },
1855     initEvents : function() {
1856         
1857        // Roo.log("ADD event");
1858        // Roo.log(this.triggerEl.dom);
1859         this.triggerEl.on('click', this.onTriggerPress, this);
1860         this.triggerEl.addClass('dropdown-toggle');
1861         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1862
1863         this.el.on("mouseover", this.onMouseOver, this);
1864         this.el.on("mouseout", this.onMouseOut, this);
1865         
1866         
1867     },
1868     findTargetItem : function(e){
1869         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1870         if(!t){
1871             return false;
1872         }
1873         //Roo.log(t);         Roo.log(t.id);
1874         if(t && t.id){
1875             //Roo.log(this.menuitems);
1876             return this.menuitems.get(t.id);
1877             
1878             //return this.items.get(t.menuItemId);
1879         }
1880         
1881         return false;
1882     },
1883     onClick : function(e){
1884         Roo.log("menu.onClick");
1885         var t = this.findTargetItem(e);
1886         if(!t || t.isContainer){
1887             return;
1888         }
1889         Roo.log(e);
1890         /*
1891         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1892             if(t == this.activeItem && t.shouldDeactivate(e)){
1893                 this.activeItem.deactivate();
1894                 delete this.activeItem;
1895                 return;
1896             }
1897             if(t.canActivate){
1898                 this.setActiveItem(t, true);
1899             }
1900             return;
1901             
1902             
1903         }
1904         */
1905        
1906         Roo.log('pass click event');
1907         
1908         t.onClick(e);
1909         
1910         this.fireEvent("click", this, t, e);
1911         
1912         this.hide();
1913     },
1914      onMouseOver : function(e){
1915         var t  = this.findTargetItem(e);
1916         //Roo.log(t);
1917         //if(t){
1918         //    if(t.canActivate && !t.disabled){
1919         //        this.setActiveItem(t, true);
1920         //    }
1921         //}
1922         
1923         this.fireEvent("mouseover", this, e, t);
1924     },
1925     isVisible : function(){
1926         return !this.hidden;
1927     },
1928      onMouseOut : function(e){
1929         var t  = this.findTargetItem(e);
1930         
1931         //if(t ){
1932         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1933         //        this.activeItem.deactivate();
1934         //        delete this.activeItem;
1935         //    }
1936         //}
1937         this.fireEvent("mouseout", this, e, t);
1938     },
1939     
1940     
1941     /**
1942      * Displays this menu relative to another element
1943      * @param {String/HTMLElement/Roo.Element} element The element to align to
1944      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1945      * the element (defaults to this.defaultAlign)
1946      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1947      */
1948     show : function(el, pos, parentMenu){
1949         this.parentMenu = parentMenu;
1950         if(!this.el){
1951             this.render();
1952         }
1953         this.fireEvent("beforeshow", this);
1954         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1955     },
1956      /**
1957      * Displays this menu at a specific xy position
1958      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1959      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1960      */
1961     showAt : function(xy, parentMenu, /* private: */_e){
1962         this.parentMenu = parentMenu;
1963         if(!this.el){
1964             this.render();
1965         }
1966         if(_e !== false){
1967             this.fireEvent("beforeshow", this);
1968             //xy = this.el.adjustForConstraints(xy);
1969         }
1970         
1971         //this.el.show();
1972         this.hideMenuItems();
1973         this.hidden = false;
1974         this.triggerEl.addClass('open');
1975         
1976         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1977             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1978         }
1979         
1980         this.el.setXY(xy);
1981         this.focus();
1982         this.fireEvent("show", this);
1983     },
1984     
1985     focus : function(){
1986         return;
1987         if(!this.hidden){
1988             this.doFocus.defer(50, this);
1989         }
1990     },
1991
1992     doFocus : function(){
1993         if(!this.hidden){
1994             this.focusEl.focus();
1995         }
1996     },
1997
1998     /**
1999      * Hides this menu and optionally all parent menus
2000      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2001      */
2002     hide : function(deep){
2003         
2004         this.hideMenuItems();
2005         if(this.el && this.isVisible()){
2006             this.fireEvent("beforehide", this);
2007             if(this.activeItem){
2008                 this.activeItem.deactivate();
2009                 this.activeItem = null;
2010             }
2011             this.triggerEl.removeClass('open');;
2012             this.hidden = true;
2013             this.fireEvent("hide", this);
2014         }
2015         if(deep === true && this.parentMenu){
2016             this.parentMenu.hide(true);
2017         }
2018     },
2019     
2020     onTriggerPress  : function(e)
2021     {
2022         
2023         Roo.log('trigger press');
2024         //Roo.log(e.getTarget());
2025        // Roo.log(this.triggerEl.dom);
2026         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2027             return;
2028         }
2029         if (this.isVisible()) {
2030             Roo.log('hide');
2031             this.hide();
2032         } else {
2033             this.show(this.triggerEl, false, false);
2034         }
2035         
2036         
2037     },
2038     
2039          
2040        
2041     
2042     hideMenuItems : function()
2043     {
2044         //$(backdrop).remove()
2045         Roo.select('.open',true).each(function(aa) {
2046             
2047             aa.removeClass('open');
2048           //var parent = getParent($(this))
2049           //var relatedTarget = { relatedTarget: this }
2050           
2051            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2052           //if (e.isDefaultPrevented()) return
2053            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2054         })
2055     },
2056     addxtypeChild : function (tree, cntr) {
2057         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2058           
2059         this.menuitems.add(comp);
2060         return comp;
2061
2062     },
2063     getEl : function()
2064     {
2065         Roo.log(this.el);
2066         return this.el;
2067     }
2068 });
2069
2070  
2071  /*
2072  * - LGPL
2073  *
2074  * menu item
2075  * 
2076  */
2077
2078
2079 /**
2080  * @class Roo.bootstrap.MenuItem
2081  * @extends Roo.bootstrap.Component
2082  * Bootstrap MenuItem class
2083  * @cfg {String} html the menu label
2084  * @cfg {String} href the link
2085  * @cfg {Boolean} preventDefault (true | false) default true
2086  * @cfg {Boolean} isContainer (true | false) default false
2087  * 
2088  * 
2089  * @constructor
2090  * Create a new MenuItem
2091  * @param {Object} config The config object
2092  */
2093
2094
2095 Roo.bootstrap.MenuItem = function(config){
2096     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2097     this.addEvents({
2098         // raw events
2099         /**
2100          * @event click
2101          * The raw click event for the entire grid.
2102          * @param {Roo.bootstrap.MenuItem} this
2103          * @param {Roo.EventObject} e
2104          */
2105         "click" : true
2106     });
2107 };
2108
2109 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2110     
2111     href : false,
2112     html : false,
2113     preventDefault: true,
2114     isContainer : false,
2115     
2116     getAutoCreate : function(){
2117         
2118         if(this.isContainer){
2119             return {
2120                 tag: 'li',
2121                 cls: 'dropdown-menu-item'
2122             };
2123         }
2124         
2125         var cfg= {
2126             tag: 'li',
2127             cls: 'dropdown-menu-item',
2128             cn: [
2129                     {
2130                         tag : 'a',
2131                         href : '#',
2132                         html : 'Link'
2133                     }
2134                 ]
2135         };
2136         if (this.parent().type == 'treeview') {
2137             cfg.cls = 'treeview-menu';
2138         }
2139         
2140         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2141         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2142         return cfg;
2143     },
2144     
2145     initEvents: function() {
2146         
2147         //this.el.select('a').on('click', this.onClick, this);
2148         
2149     },
2150     onClick : function(e)
2151     {
2152         Roo.log('item on click ');
2153         //if(this.preventDefault){
2154         //    e.preventDefault();
2155         //}
2156         //this.parent().hideMenuItems();
2157         
2158         this.fireEvent('click', this, e);
2159     },
2160     getEl : function()
2161     {
2162         return this.el;
2163     }
2164 });
2165
2166  
2167
2168  /*
2169  * - LGPL
2170  *
2171  * menu separator
2172  * 
2173  */
2174
2175
2176 /**
2177  * @class Roo.bootstrap.MenuSeparator
2178  * @extends Roo.bootstrap.Component
2179  * Bootstrap MenuSeparator class
2180  * 
2181  * @constructor
2182  * Create a new MenuItem
2183  * @param {Object} config The config object
2184  */
2185
2186
2187 Roo.bootstrap.MenuSeparator = function(config){
2188     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2189 };
2190
2191 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2192     
2193     getAutoCreate : function(){
2194         var cfg = {
2195             cls: 'divider',
2196             tag : 'li'
2197         };
2198         
2199         return cfg;
2200     }
2201    
2202 });
2203
2204  
2205
2206  
2207 /*
2208 * Licence: LGPL
2209 */
2210
2211 /**
2212  * @class Roo.bootstrap.Modal
2213  * @extends Roo.bootstrap.Component
2214  * Bootstrap Modal class
2215  * @cfg {String} title Title of dialog
2216  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2217  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2218  * @cfg {Boolean} specificTitle default false
2219  * @cfg {Array} buttons Array of buttons or standard button set..
2220  * @cfg {String} buttonPosition (left|right|center) default right
2221  * @cfg {Boolean} animate default true
2222  * @cfg {Boolean} allow_close default true
2223  * 
2224  * @constructor
2225  * Create a new Modal Dialog
2226  * @param {Object} config The config object
2227  */
2228
2229 Roo.bootstrap.Modal = function(config){
2230     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2231     this.addEvents({
2232         // raw events
2233         /**
2234          * @event btnclick
2235          * The raw btnclick event for the button
2236          * @param {Roo.EventObject} e
2237          */
2238         "btnclick" : true
2239     });
2240     this.buttons = this.buttons || [];
2241      
2242     if (this.tmpl) {
2243         this.tmpl = Roo.factory(this.tmpl);
2244     }
2245     
2246 };
2247
2248 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2249     
2250     title : 'test dialog',
2251    
2252     buttons : false,
2253     
2254     // set on load...
2255      
2256     html: false,
2257     
2258     tmp: false,
2259     
2260     specificTitle: false,
2261     
2262     buttonPosition: 'right',
2263     
2264     allow_close : true,
2265     
2266     animate : true,
2267     
2268     
2269      // private
2270     bodyEl:  false,
2271     footerEl:  false,
2272     titleEl:  false,
2273     closeEl:  false,
2274     
2275     
2276     onRender : function(ct, position)
2277     {
2278         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2279      
2280         if(!this.el){
2281             var cfg = Roo.apply({},  this.getAutoCreate());
2282             cfg.id = Roo.id();
2283             //if(!cfg.name){
2284             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2285             //}
2286             //if (!cfg.name.length) {
2287             //    delete cfg.name;
2288            // }
2289             if (this.cls) {
2290                 cfg.cls += ' ' + this.cls;
2291             }
2292             if (this.style) {
2293                 cfg.style = this.style;
2294             }
2295             this.el = Roo.get(document.body).createChild(cfg, position);
2296         }
2297         //var type = this.el.dom.type;
2298         
2299         
2300         
2301         
2302         if(this.tabIndex !== undefined){
2303             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2304         }
2305         
2306         
2307         this.bodyEl = this.el.select('.modal-body',true).first();
2308         this.closeEl = this.el.select('.modal-header .close', true).first();
2309         this.footerEl = this.el.select('.modal-footer',true).first();
2310         this.titleEl = this.el.select('.modal-title',true).first();
2311         
2312         
2313          
2314         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2315         this.maskEl.enableDisplayMode("block");
2316         this.maskEl.hide();
2317         //this.el.addClass("x-dlg-modal");
2318     
2319         if (this.buttons.length) {
2320             Roo.each(this.buttons, function(bb) {
2321                 b = Roo.apply({}, bb);
2322                 b.xns = b.xns || Roo.bootstrap;
2323                 b.xtype = b.xtype || 'Button';
2324                 if (typeof(b.listeners) == 'undefined') {
2325                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2326                 }
2327                 
2328                 var btn = Roo.factory(b);
2329                 
2330                 btn.onRender(this.el.select('.modal-footer div').first());
2331                 
2332             },this);
2333         }
2334         // render the children.
2335         var nitems = [];
2336         
2337         if(typeof(this.items) != 'undefined'){
2338             var items = this.items;
2339             delete this.items;
2340
2341             for(var i =0;i < items.length;i++) {
2342                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2343             }
2344         }
2345         
2346         this.items = nitems;
2347         
2348         // where are these used - they used to be body/close/footer
2349         
2350        
2351         this.initEvents();
2352         //this.el.addClass([this.fieldClass, this.cls]);
2353         
2354     },
2355     getAutoCreate : function(){
2356         
2357         
2358         var bdy = {
2359                 cls : 'modal-body',
2360                 html : this.html || ''
2361         };
2362         
2363         var title = {
2364             tag: 'h4',
2365             cls : 'modal-title',
2366             html : this.title
2367         };
2368         
2369         if(this.specificTitle){
2370             title = this.title;
2371             
2372         };
2373         
2374         var header = [];
2375         if (this.allow_close) {
2376             header.push({
2377                 tag: 'button',
2378                 cls : 'close',
2379                 html : '&times'
2380             });
2381         }
2382         header.push(title);
2383         
2384         var modal = {
2385             cls: "modal",
2386             style : 'display: none',
2387             cn : [
2388                 {
2389                     cls: "modal-dialog",
2390                     cn : [
2391                         {
2392                             cls : "modal-content",
2393                             cn : [
2394                                 {
2395                                     cls : 'modal-header',
2396                                     cn : header
2397                                 },
2398                                 bdy,
2399                                 {
2400                                     cls : 'modal-footer',
2401                                     cn : [
2402                                         {
2403                                             tag: 'div',
2404                                             cls: 'btn-' + this.buttonPosition
2405                                         }
2406                                     ]
2407                                     
2408                                 }
2409                                 
2410                                 
2411                             ]
2412                             
2413                         }
2414                     ]
2415                         
2416                 }
2417             ]
2418         };
2419         
2420         if(this.animate){
2421             modal.cls += ' fade';
2422         }
2423         
2424         return modal;
2425           
2426     },
2427     getChildContainer : function() {
2428          
2429          return this.bodyEl;
2430         
2431     },
2432     getButtonContainer : function() {
2433          return this.el.select('.modal-footer div',true).first();
2434         
2435     },
2436     initEvents : function()
2437     {
2438         if (this.allow_close) {
2439             this.closeEl.on('click', this.hide, this);
2440         }
2441
2442     },
2443     show : function() {
2444         
2445         if (!this.rendered) {
2446             this.render();
2447         }
2448         
2449         this.el.setStyle('display', 'block');
2450         
2451         if(this.animate){
2452             var _this = this;
2453             (function(){ _this.el.addClass('in'); }).defer(50);
2454         }else{
2455             this.el.addClass('in');
2456         }
2457         
2458         // not sure how we can show data in here.. 
2459         //if (this.tmpl) {
2460         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2461         //}
2462         
2463         Roo.get(document.body).addClass("x-body-masked");
2464         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2465         this.maskEl.show();
2466         this.el.setStyle('zIndex', '10001');
2467        
2468         this.fireEvent('show', this);
2469         
2470         
2471     },
2472     hide : function()
2473     {
2474         this.maskEl.hide();
2475         Roo.get(document.body).removeClass("x-body-masked");
2476         this.el.removeClass('in');
2477         
2478         if(this.animate){
2479             var _this = this;
2480             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2481         }else{
2482             this.el.setStyle('display', 'none');
2483         }
2484         
2485         this.fireEvent('hide', this);
2486     },
2487     
2488     addButton : function(str, cb)
2489     {
2490          
2491         
2492         var b = Roo.apply({}, { html : str } );
2493         b.xns = b.xns || Roo.bootstrap;
2494         b.xtype = b.xtype || 'Button';
2495         if (typeof(b.listeners) == 'undefined') {
2496             b.listeners = { click : cb.createDelegate(this)  };
2497         }
2498         
2499         var btn = Roo.factory(b);
2500            
2501         btn.onRender(this.el.select('.modal-footer div').first());
2502         
2503         return btn;   
2504        
2505     },
2506     
2507     setDefaultButton : function(btn)
2508     {
2509         //this.el.select('.modal-footer').()
2510     },
2511     resizeTo: function(w,h)
2512     {
2513         // skip..
2514     },
2515     setContentSize  : function(w, h)
2516     {
2517         
2518     },
2519     onButtonClick: function(btn,e)
2520     {
2521         //Roo.log([a,b,c]);
2522         this.fireEvent('btnclick', btn.name, e);
2523     },
2524      /**
2525      * Set the title of the Dialog
2526      * @param {String} str new Title
2527      */
2528     setTitle: function(str) {
2529         this.titleEl.dom.innerHTML = str;    
2530     },
2531     /**
2532      * Set the body of the Dialog
2533      * @param {String} str new Title
2534      */
2535     setBody: function(str) {
2536         this.bodyEl.dom.innerHTML = str;    
2537     },
2538     /**
2539      * Set the body of the Dialog using the template
2540      * @param {Obj} data - apply this data to the template and replace the body contents.
2541      */
2542     applyBody: function(obj)
2543     {
2544         if (!this.tmpl) {
2545             Roo.log("Error - using apply Body without a template");
2546             //code
2547         }
2548         this.tmpl.overwrite(this.bodyEl, obj);
2549     }
2550     
2551 });
2552
2553
2554 Roo.apply(Roo.bootstrap.Modal,  {
2555     /**
2556          * Button config that displays a single OK button
2557          * @type Object
2558          */
2559         OK :  [{
2560             name : 'ok',
2561             weight : 'primary',
2562             html : 'OK'
2563         }], 
2564         /**
2565          * Button config that displays Yes and No buttons
2566          * @type Object
2567          */
2568         YESNO : [
2569             {
2570                 name  : 'no',
2571                 html : 'No'
2572             },
2573             {
2574                 name  :'yes',
2575                 weight : 'primary',
2576                 html : 'Yes'
2577             }
2578         ],
2579         
2580         /**
2581          * Button config that displays OK and Cancel buttons
2582          * @type Object
2583          */
2584         OKCANCEL : [
2585             {
2586                name : 'cancel',
2587                 html : 'Cancel'
2588             },
2589             {
2590                 name : 'ok',
2591                 weight : 'primary',
2592                 html : 'OK'
2593             }
2594         ],
2595         /**
2596          * Button config that displays Yes, No and Cancel buttons
2597          * @type Object
2598          */
2599         YESNOCANCEL : [
2600             {
2601                 name : 'yes',
2602                 weight : 'primary',
2603                 html : 'Yes'
2604             },
2605             {
2606                 name : 'no',
2607                 html : 'No'
2608             },
2609             {
2610                 name : 'cancel',
2611                 html : 'Cancel'
2612             }
2613         ]
2614 });
2615  
2616  /*
2617  * - LGPL
2618  *
2619  * messagebox - can be used as a replace
2620  * 
2621  */
2622 /**
2623  * @class Roo.MessageBox
2624  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2625  * Example usage:
2626  *<pre><code>
2627 // Basic alert:
2628 Roo.Msg.alert('Status', 'Changes saved successfully.');
2629
2630 // Prompt for user data:
2631 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2632     if (btn == 'ok'){
2633         // process text value...
2634     }
2635 });
2636
2637 // Show a dialog using config options:
2638 Roo.Msg.show({
2639    title:'Save Changes?',
2640    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2641    buttons: Roo.Msg.YESNOCANCEL,
2642    fn: processResult,
2643    animEl: 'elId'
2644 });
2645 </code></pre>
2646  * @singleton
2647  */
2648 Roo.bootstrap.MessageBox = function(){
2649     var dlg, opt, mask, waitTimer;
2650     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2651     var buttons, activeTextEl, bwidth;
2652
2653     
2654     // private
2655     var handleButton = function(button){
2656         dlg.hide();
2657         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2658     };
2659
2660     // private
2661     var handleHide = function(){
2662         if(opt && opt.cls){
2663             dlg.el.removeClass(opt.cls);
2664         }
2665         //if(waitTimer){
2666         //    Roo.TaskMgr.stop(waitTimer);
2667         //    waitTimer = null;
2668         //}
2669     };
2670
2671     // private
2672     var updateButtons = function(b){
2673         var width = 0;
2674         if(!b){
2675             buttons["ok"].hide();
2676             buttons["cancel"].hide();
2677             buttons["yes"].hide();
2678             buttons["no"].hide();
2679             //dlg.footer.dom.style.display = 'none';
2680             return width;
2681         }
2682         dlg.footerEl.dom.style.display = '';
2683         for(var k in buttons){
2684             if(typeof buttons[k] != "function"){
2685                 if(b[k]){
2686                     buttons[k].show();
2687                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2688                     width += buttons[k].el.getWidth()+15;
2689                 }else{
2690                     buttons[k].hide();
2691                 }
2692             }
2693         }
2694         return width;
2695     };
2696
2697     // private
2698     var handleEsc = function(d, k, e){
2699         if(opt && opt.closable !== false){
2700             dlg.hide();
2701         }
2702         if(e){
2703             e.stopEvent();
2704         }
2705     };
2706
2707     return {
2708         /**
2709          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2710          * @return {Roo.BasicDialog} The BasicDialog element
2711          */
2712         getDialog : function(){
2713            if(!dlg){
2714                 dlg = new Roo.bootstrap.Modal( {
2715                     //draggable: true,
2716                     //resizable:false,
2717                     //constraintoviewport:false,
2718                     //fixedcenter:true,
2719                     //collapsible : false,
2720                     //shim:true,
2721                     //modal: true,
2722                   //  width:400,
2723                   //  height:100,
2724                     //buttonAlign:"center",
2725                     closeClick : function(){
2726                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2727                             handleButton("no");
2728                         }else{
2729                             handleButton("cancel");
2730                         }
2731                     }
2732                 });
2733                 dlg.render();
2734                 dlg.on("hide", handleHide);
2735                 mask = dlg.mask;
2736                 //dlg.addKeyListener(27, handleEsc);
2737                 buttons = {};
2738                 this.buttons = buttons;
2739                 var bt = this.buttonText;
2740                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2741                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2742                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2743                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2744                 Roo.log(buttons)
2745                 bodyEl = dlg.bodyEl.createChild({
2746
2747                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2748                         '<textarea class="roo-mb-textarea"></textarea>' +
2749                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2750                 });
2751                 msgEl = bodyEl.dom.firstChild;
2752                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2753                 textboxEl.enableDisplayMode();
2754                 textboxEl.addKeyListener([10,13], function(){
2755                     if(dlg.isVisible() && opt && opt.buttons){
2756                         if(opt.buttons.ok){
2757                             handleButton("ok");
2758                         }else if(opt.buttons.yes){
2759                             handleButton("yes");
2760                         }
2761                     }
2762                 });
2763                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2764                 textareaEl.enableDisplayMode();
2765                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2766                 progressEl.enableDisplayMode();
2767                 var pf = progressEl.dom.firstChild;
2768                 if (pf) {
2769                     pp = Roo.get(pf.firstChild);
2770                     pp.setHeight(pf.offsetHeight);
2771                 }
2772                 
2773             }
2774             return dlg;
2775         },
2776
2777         /**
2778          * Updates the message box body text
2779          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2780          * the XHTML-compliant non-breaking space character '&amp;#160;')
2781          * @return {Roo.MessageBox} This message box
2782          */
2783         updateText : function(text){
2784             if(!dlg.isVisible() && !opt.width){
2785                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2786             }
2787             msgEl.innerHTML = text || '&#160;';
2788       
2789             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2790             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2791             var w = Math.max(
2792                     Math.min(opt.width || cw , this.maxWidth), 
2793                     Math.max(opt.minWidth || this.minWidth, bwidth)
2794             );
2795             if(opt.prompt){
2796                 activeTextEl.setWidth(w);
2797             }
2798             if(dlg.isVisible()){
2799                 dlg.fixedcenter = false;
2800             }
2801             // to big, make it scroll. = But as usual stupid IE does not support
2802             // !important..
2803             
2804             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2805                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2806                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2807             } else {
2808                 bodyEl.dom.style.height = '';
2809                 bodyEl.dom.style.overflowY = '';
2810             }
2811             if (cw > w) {
2812                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2813             } else {
2814                 bodyEl.dom.style.overflowX = '';
2815             }
2816             
2817             dlg.setContentSize(w, bodyEl.getHeight());
2818             if(dlg.isVisible()){
2819                 dlg.fixedcenter = true;
2820             }
2821             return this;
2822         },
2823
2824         /**
2825          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2826          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2827          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2828          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2829          * @return {Roo.MessageBox} This message box
2830          */
2831         updateProgress : function(value, text){
2832             if(text){
2833                 this.updateText(text);
2834             }
2835             if (pp) { // weird bug on my firefox - for some reason this is not defined
2836                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2837             }
2838             return this;
2839         },        
2840
2841         /**
2842          * Returns true if the message box is currently displayed
2843          * @return {Boolean} True if the message box is visible, else false
2844          */
2845         isVisible : function(){
2846             return dlg && dlg.isVisible();  
2847         },
2848
2849         /**
2850          * Hides the message box if it is displayed
2851          */
2852         hide : function(){
2853             if(this.isVisible()){
2854                 dlg.hide();
2855             }  
2856         },
2857
2858         /**
2859          * Displays a new message box, or reinitializes an existing message box, based on the config options
2860          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2861          * The following config object properties are supported:
2862          * <pre>
2863 Property    Type             Description
2864 ----------  ---------------  ------------------------------------------------------------------------------------
2865 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2866                                    closes (defaults to undefined)
2867 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2868                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2869 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2870                                    progress and wait dialogs will ignore this property and always hide the
2871                                    close button as they can only be closed programmatically.
2872 cls               String           A custom CSS class to apply to the message box element
2873 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2874                                    displayed (defaults to 75)
2875 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2876                                    function will be btn (the name of the button that was clicked, if applicable,
2877                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2878                                    Progress and wait dialogs will ignore this option since they do not respond to
2879                                    user actions and can only be closed programmatically, so any required function
2880                                    should be called by the same code after it closes the dialog.
2881 icon              String           A CSS class that provides a background image to be used as an icon for
2882                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2883 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2884 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2885 modal             Boolean          False to allow user interaction with the page while the message box is
2886                                    displayed (defaults to true)
2887 msg               String           A string that will replace the existing message box body text (defaults
2888                                    to the XHTML-compliant non-breaking space character '&#160;')
2889 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2890 progress          Boolean          True to display a progress bar (defaults to false)
2891 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2892 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2893 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2894 title             String           The title text
2895 value             String           The string value to set into the active textbox element if displayed
2896 wait              Boolean          True to display a progress bar (defaults to false)
2897 width             Number           The width of the dialog in pixels
2898 </pre>
2899          *
2900          * Example usage:
2901          * <pre><code>
2902 Roo.Msg.show({
2903    title: 'Address',
2904    msg: 'Please enter your address:',
2905    width: 300,
2906    buttons: Roo.MessageBox.OKCANCEL,
2907    multiline: true,
2908    fn: saveAddress,
2909    animEl: 'addAddressBtn'
2910 });
2911 </code></pre>
2912          * @param {Object} config Configuration options
2913          * @return {Roo.MessageBox} This message box
2914          */
2915         show : function(options)
2916         {
2917             
2918             // this causes nightmares if you show one dialog after another
2919             // especially on callbacks..
2920              
2921             if(this.isVisible()){
2922                 
2923                 this.hide();
2924                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2925                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2926                 Roo.log("New Dialog Message:" +  options.msg )
2927                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2928                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2929                 
2930             }
2931             var d = this.getDialog();
2932             opt = options;
2933             d.setTitle(opt.title || "&#160;");
2934             d.closeEl.setDisplayed(opt.closable !== false);
2935             activeTextEl = textboxEl;
2936             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2937             if(opt.prompt){
2938                 if(opt.multiline){
2939                     textboxEl.hide();
2940                     textareaEl.show();
2941                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2942                         opt.multiline : this.defaultTextHeight);
2943                     activeTextEl = textareaEl;
2944                 }else{
2945                     textboxEl.show();
2946                     textareaEl.hide();
2947                 }
2948             }else{
2949                 textboxEl.hide();
2950                 textareaEl.hide();
2951             }
2952             progressEl.setDisplayed(opt.progress === true);
2953             this.updateProgress(0);
2954             activeTextEl.dom.value = opt.value || "";
2955             if(opt.prompt){
2956                 dlg.setDefaultButton(activeTextEl);
2957             }else{
2958                 var bs = opt.buttons;
2959                 var db = null;
2960                 if(bs && bs.ok){
2961                     db = buttons["ok"];
2962                 }else if(bs && bs.yes){
2963                     db = buttons["yes"];
2964                 }
2965                 dlg.setDefaultButton(db);
2966             }
2967             bwidth = updateButtons(opt.buttons);
2968             this.updateText(opt.msg);
2969             if(opt.cls){
2970                 d.el.addClass(opt.cls);
2971             }
2972             d.proxyDrag = opt.proxyDrag === true;
2973             d.modal = opt.modal !== false;
2974             d.mask = opt.modal !== false ? mask : false;
2975             if(!d.isVisible()){
2976                 // force it to the end of the z-index stack so it gets a cursor in FF
2977                 document.body.appendChild(dlg.el.dom);
2978                 d.animateTarget = null;
2979                 d.show(options.animEl);
2980             }
2981             return this;
2982         },
2983
2984         /**
2985          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2986          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2987          * and closing the message box when the process is complete.
2988          * @param {String} title The title bar text
2989          * @param {String} msg The message box body text
2990          * @return {Roo.MessageBox} This message box
2991          */
2992         progress : function(title, msg){
2993             this.show({
2994                 title : title,
2995                 msg : msg,
2996                 buttons: false,
2997                 progress:true,
2998                 closable:false,
2999                 minWidth: this.minProgressWidth,
3000                 modal : true
3001             });
3002             return this;
3003         },
3004
3005         /**
3006          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3007          * If a callback function is passed it will be called after the user clicks the button, and the
3008          * id of the button that was clicked will be passed as the only parameter to the callback
3009          * (could also be the top-right close button).
3010          * @param {String} title The title bar text
3011          * @param {String} msg The message box body text
3012          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3013          * @param {Object} scope (optional) The scope of the callback function
3014          * @return {Roo.MessageBox} This message box
3015          */
3016         alert : function(title, msg, fn, scope){
3017             this.show({
3018                 title : title,
3019                 msg : msg,
3020                 buttons: this.OK,
3021                 fn: fn,
3022                 scope : scope,
3023                 modal : true
3024             });
3025             return this;
3026         },
3027
3028         /**
3029          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3030          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3031          * You are responsible for closing the message box when the process is complete.
3032          * @param {String} msg The message box body text
3033          * @param {String} title (optional) The title bar text
3034          * @return {Roo.MessageBox} This message box
3035          */
3036         wait : function(msg, title){
3037             this.show({
3038                 title : title,
3039                 msg : msg,
3040                 buttons: false,
3041                 closable:false,
3042                 progress:true,
3043                 modal:true,
3044                 width:300,
3045                 wait:true
3046             });
3047             waitTimer = Roo.TaskMgr.start({
3048                 run: function(i){
3049                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3050                 },
3051                 interval: 1000
3052             });
3053             return this;
3054         },
3055
3056         /**
3057          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3058          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3059          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3060          * @param {String} title The title bar text
3061          * @param {String} msg The message box body text
3062          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3063          * @param {Object} scope (optional) The scope of the callback function
3064          * @return {Roo.MessageBox} This message box
3065          */
3066         confirm : function(title, msg, fn, scope){
3067             this.show({
3068                 title : title,
3069                 msg : msg,
3070                 buttons: this.YESNO,
3071                 fn: fn,
3072                 scope : scope,
3073                 modal : true
3074             });
3075             return this;
3076         },
3077
3078         /**
3079          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3080          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3081          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3082          * (could also be the top-right close button) and the text that was entered will be passed as the two
3083          * parameters to the callback.
3084          * @param {String} title The title bar text
3085          * @param {String} msg The message box body text
3086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3087          * @param {Object} scope (optional) The scope of the callback function
3088          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3089          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3090          * @return {Roo.MessageBox} This message box
3091          */
3092         prompt : function(title, msg, fn, scope, multiline){
3093             this.show({
3094                 title : title,
3095                 msg : msg,
3096                 buttons: this.OKCANCEL,
3097                 fn: fn,
3098                 minWidth:250,
3099                 scope : scope,
3100                 prompt:true,
3101                 multiline: multiline,
3102                 modal : true
3103             });
3104             return this;
3105         },
3106
3107         /**
3108          * Button config that displays a single OK button
3109          * @type Object
3110          */
3111         OK : {ok:true},
3112         /**
3113          * Button config that displays Yes and No buttons
3114          * @type Object
3115          */
3116         YESNO : {yes:true, no:true},
3117         /**
3118          * Button config that displays OK and Cancel buttons
3119          * @type Object
3120          */
3121         OKCANCEL : {ok:true, cancel:true},
3122         /**
3123          * Button config that displays Yes, No and Cancel buttons
3124          * @type Object
3125          */
3126         YESNOCANCEL : {yes:true, no:true, cancel:true},
3127
3128         /**
3129          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3130          * @type Number
3131          */
3132         defaultTextHeight : 75,
3133         /**
3134          * The maximum width in pixels of the message box (defaults to 600)
3135          * @type Number
3136          */
3137         maxWidth : 600,
3138         /**
3139          * The minimum width in pixels of the message box (defaults to 100)
3140          * @type Number
3141          */
3142         minWidth : 100,
3143         /**
3144          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3145          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3146          * @type Number
3147          */
3148         minProgressWidth : 250,
3149         /**
3150          * An object containing the default button text strings that can be overriden for localized language support.
3151          * Supported properties are: ok, cancel, yes and no.
3152          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3153          * @type Object
3154          */
3155         buttonText : {
3156             ok : "OK",
3157             cancel : "Cancel",
3158             yes : "Yes",
3159             no : "No"
3160         }
3161     };
3162 }();
3163
3164 /**
3165  * Shorthand for {@link Roo.MessageBox}
3166  */
3167 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3168 Roo.Msg = Roo.Msg || Roo.MessageBox;
3169 /*
3170  * - LGPL
3171  *
3172  * navbar
3173  * 
3174  */
3175
3176 /**
3177  * @class Roo.bootstrap.Navbar
3178  * @extends Roo.bootstrap.Component
3179  * Bootstrap Navbar class
3180
3181  * @constructor
3182  * Create a new Navbar
3183  * @param {Object} config The config object
3184  */
3185
3186
3187 Roo.bootstrap.Navbar = function(config){
3188     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3189     
3190 };
3191
3192 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3193     
3194     
3195    
3196     // private
3197     navItems : false,
3198     loadMask : false,
3199     
3200     
3201     getAutoCreate : function(){
3202         
3203         
3204         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3205         
3206     },
3207     
3208     initEvents :function ()
3209     {
3210         //Roo.log(this.el.select('.navbar-toggle',true));
3211         this.el.select('.navbar-toggle',true).on('click', function() {
3212            // Roo.log('click');
3213             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3214         }, this);
3215         
3216         var mark = {
3217             tag: "div",
3218             cls:"x-dlg-mask"
3219         }
3220         
3221         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3222         
3223         var size = this.el.getSize();
3224         this.maskEl.setSize(size.width, size.height);
3225         this.maskEl.enableDisplayMode("block");
3226         this.maskEl.hide();
3227         
3228         if(this.loadMask){
3229             this.maskEl.show();
3230         }
3231     },
3232     
3233     
3234     getChildContainer : function()
3235     {
3236         if (this.el.select('.collapse').getCount()) {
3237             return this.el.select('.collapse',true).first();
3238         }
3239         
3240         return this.el;
3241     },
3242     
3243     mask : function()
3244     {
3245         this.maskEl.show();
3246     },
3247     
3248     unmask : function()
3249     {
3250         this.maskEl.hide();
3251     } 
3252     
3253     
3254     
3255     
3256 });
3257
3258
3259
3260  
3261
3262  /*
3263  * - LGPL
3264  *
3265  * navbar
3266  * 
3267  */
3268
3269 /**
3270  * @class Roo.bootstrap.NavSimplebar
3271  * @extends Roo.bootstrap.Navbar
3272  * Bootstrap Sidebar class
3273  *
3274  * @cfg {Boolean} inverse is inverted color
3275  * 
3276  * @cfg {String} type (nav | pills | tabs)
3277  * @cfg {Boolean} arrangement stacked | justified
3278  * @cfg {String} align (left | right) alignment
3279  * 
3280  * @cfg {Boolean} main (true|false) main nav bar? default false
3281  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3282  * 
3283  * @cfg {String} tag (header|footer|nav|div) default is nav 
3284
3285  * 
3286  * 
3287  * 
3288  * @constructor
3289  * Create a new Sidebar
3290  * @param {Object} config The config object
3291  */
3292
3293
3294 Roo.bootstrap.NavSimplebar = function(config){
3295     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3296 };
3297
3298 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3299     
3300     inverse: false,
3301     
3302     type: false,
3303     arrangement: '',
3304     align : false,
3305     
3306     
3307     
3308     main : false,
3309     
3310     
3311     tag : false,
3312     
3313     
3314     getAutoCreate : function(){
3315         
3316         
3317         var cfg = {
3318             tag : this.tag || 'div',
3319             cls : 'navbar'
3320         };
3321           
3322         
3323         cfg.cn = [
3324             {
3325                 cls: 'nav',
3326                 tag : 'ul'
3327             }
3328         ];
3329         
3330          
3331         this.type = this.type || 'nav';
3332         if (['tabs','pills'].indexOf(this.type)!==-1) {
3333             cfg.cn[0].cls += ' nav-' + this.type
3334         
3335         
3336         } else {
3337             if (this.type!=='nav') {
3338                 Roo.log('nav type must be nav/tabs/pills')
3339             }
3340             cfg.cn[0].cls += ' navbar-nav'
3341         }
3342         
3343         
3344         
3345         
3346         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3347             cfg.cn[0].cls += ' nav-' + this.arrangement;
3348         }
3349         
3350         
3351         if (this.align === 'right') {
3352             cfg.cn[0].cls += ' navbar-right';
3353         }
3354         
3355         if (this.inverse) {
3356             cfg.cls += ' navbar-inverse';
3357             
3358         }
3359         
3360         
3361         return cfg;
3362     
3363         
3364     }
3365     
3366     
3367     
3368 });
3369
3370
3371
3372  
3373
3374  
3375        /*
3376  * - LGPL
3377  *
3378  * navbar
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.NavHeaderbar
3384  * @extends Roo.bootstrap.NavSimplebar
3385  * Bootstrap Sidebar class
3386  *
3387  * @cfg {String} brand what is brand
3388  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3389  * @cfg {String} brand_href href of the brand
3390  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3391  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3392  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3393  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3394  * 
3395  * @constructor
3396  * Create a new Sidebar
3397  * @param {Object} config The config object
3398  */
3399
3400
3401 Roo.bootstrap.NavHeaderbar = function(config){
3402     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3403       
3404 };
3405
3406 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3407     
3408     position: '',
3409     brand: '',
3410     brand_href: false,
3411     srButton : true,
3412     autohide : false,
3413     desktopCenter : false,
3414    
3415     
3416     getAutoCreate : function(){
3417         
3418         var   cfg = {
3419             tag: this.nav || 'nav',
3420             cls: 'navbar',
3421             role: 'navigation',
3422             cn: []
3423         };
3424         
3425         var cn = cfg.cn;
3426         if (this.desktopCenter) {
3427             cn.push({cls : 'container', cn : []});
3428             cn = cn[0].cn;
3429         }
3430         
3431         if(this.srButton){
3432             cn.push({
3433                 tag: 'div',
3434                 cls: 'navbar-header',
3435                 cn: [
3436                     {
3437                         tag: 'button',
3438                         type: 'button',
3439                         cls: 'navbar-toggle',
3440                         'data-toggle': 'collapse',
3441                         cn: [
3442                             {
3443                                 tag: 'span',
3444                                 cls: 'sr-only',
3445                                 html: 'Toggle navigation'
3446                             },
3447                             {
3448                                 tag: 'span',
3449                                 cls: 'icon-bar'
3450                             },
3451                             {
3452                                 tag: 'span',
3453                                 cls: 'icon-bar'
3454                             },
3455                             {
3456                                 tag: 'span',
3457                                 cls: 'icon-bar'
3458                             }
3459                         ]
3460                     }
3461                 ]
3462             });
3463         }
3464         
3465         cn.push({
3466             tag: 'div',
3467             cls: 'collapse navbar-collapse',
3468             cn : []
3469         });
3470         
3471         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3472         
3473         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3474             cfg.cls += ' navbar-' + this.position;
3475             
3476             // tag can override this..
3477             
3478             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3479         }
3480         
3481         if (this.brand !== '') {
3482             cn[0].cn.push({
3483                 tag: 'a',
3484                 href: this.brand_href ? this.brand_href : '#',
3485                 cls: 'navbar-brand',
3486                 cn: [
3487                 this.brand
3488                 ]
3489             });
3490         }
3491         
3492         if(this.main){
3493             cfg.cls += ' main-nav';
3494         }
3495         
3496         
3497         return cfg;
3498
3499         
3500     },
3501     getHeaderChildContainer : function()
3502     {
3503         if (this.el.select('.navbar-header').getCount()) {
3504             return this.el.select('.navbar-header',true).first();
3505         }
3506         
3507         return this.getChildContainer();
3508     },
3509     
3510     
3511     initEvents : function()
3512     {
3513         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3514         
3515         if (this.autohide) {
3516             
3517             var prevScroll = 0;
3518             var ft = this.el;
3519             
3520             Roo.get(document).on('scroll',function(e) {
3521                 var ns = Roo.get(document).getScroll().top;
3522                 var os = prevScroll;
3523                 prevScroll = ns;
3524                 
3525                 if(ns > os){
3526                     ft.removeClass('slideDown');
3527                     ft.addClass('slideUp');
3528                     return;
3529                 }
3530                 ft.removeClass('slideUp');
3531                 ft.addClass('slideDown');
3532                  
3533               
3534           },this);
3535         }
3536     }    
3537     
3538 });
3539
3540
3541
3542  
3543
3544  /*
3545  * - LGPL
3546  *
3547  * navbar
3548  * 
3549  */
3550
3551 /**
3552  * @class Roo.bootstrap.NavSidebar
3553  * @extends Roo.bootstrap.Navbar
3554  * Bootstrap Sidebar class
3555  * 
3556  * @constructor
3557  * Create a new Sidebar
3558  * @param {Object} config The config object
3559  */
3560
3561
3562 Roo.bootstrap.NavSidebar = function(config){
3563     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3564 };
3565
3566 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3567     
3568     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3569     
3570     getAutoCreate : function(){
3571         
3572         
3573         return  {
3574             tag: 'div',
3575             cls: 'sidebar sidebar-nav'
3576         };
3577     
3578         
3579     }
3580     
3581     
3582     
3583 });
3584
3585
3586
3587  
3588
3589  /*
3590  * - LGPL
3591  *
3592  * nav group
3593  * 
3594  */
3595
3596 /**
3597  * @class Roo.bootstrap.NavGroup
3598  * @extends Roo.bootstrap.Component
3599  * Bootstrap NavGroup class
3600  * @cfg {String} align left | right
3601  * @cfg {Boolean} inverse false | true
3602  * @cfg {String} type (nav|pills|tab) default nav
3603  * @cfg {String} navId - reference Id for navbar.
3604
3605  * 
3606  * @constructor
3607  * Create a new nav group
3608  * @param {Object} config The config object
3609  */
3610
3611 Roo.bootstrap.NavGroup = function(config){
3612     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3613     this.navItems = [];
3614    
3615     Roo.bootstrap.NavGroup.register(this);
3616      this.addEvents({
3617         /**
3618              * @event changed
3619              * Fires when the active item changes
3620              * @param {Roo.bootstrap.NavGroup} this
3621              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3622              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3623          */
3624         'changed': true
3625      });
3626     
3627 };
3628
3629 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3630     
3631     align: '',
3632     inverse: false,
3633     form: false,
3634     type: 'nav',
3635     navId : '',
3636     // private
3637     
3638     navItems : false, 
3639     
3640     getAutoCreate : function()
3641     {
3642         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3643         
3644         cfg = {
3645             tag : 'ul',
3646             cls: 'nav' 
3647         }
3648         
3649         if (['tabs','pills'].indexOf(this.type)!==-1) {
3650             cfg.cls += ' nav-' + this.type
3651         } else {
3652             if (this.type!=='nav') {
3653                 Roo.log('nav type must be nav/tabs/pills')
3654             }
3655             cfg.cls += ' navbar-nav'
3656         }
3657         
3658         if (this.parent().sidebar) {
3659             cfg = {
3660                 tag: 'ul',
3661                 cls: 'dashboard-menu sidebar-menu'
3662             }
3663             
3664             return cfg;
3665         }
3666         
3667         if (this.form === true) {
3668             cfg = {
3669                 tag: 'form',
3670                 cls: 'navbar-form'
3671             }
3672             
3673             if (this.align === 'right') {
3674                 cfg.cls += ' navbar-right';
3675             } else {
3676                 cfg.cls += ' navbar-left';
3677             }
3678         }
3679         
3680         if (this.align === 'right') {
3681             cfg.cls += ' navbar-right';
3682         }
3683         
3684         if (this.inverse) {
3685             cfg.cls += ' navbar-inverse';
3686             
3687         }
3688         
3689         
3690         return cfg;
3691     },
3692     /**
3693     * sets the active Navigation item
3694     * @param {Roo.bootstrap.NavItem} the new current navitem
3695     */
3696     setActiveItem : function(item)
3697     {
3698         var prev = false;
3699         Roo.each(this.navItems, function(v){
3700             if (v == item) {
3701                 return ;
3702             }
3703             if (v.isActive()) {
3704                 v.setActive(false, true);
3705                 prev = v;
3706                 
3707             }
3708             
3709         });
3710
3711         item.setActive(true, true);
3712         this.fireEvent('changed', this, item, prev);
3713         
3714         
3715     },
3716     /**
3717     * gets the active Navigation item
3718     * @return {Roo.bootstrap.NavItem} the current navitem
3719     */
3720     getActive : function()
3721     {
3722         
3723         var prev = false;
3724         Roo.each(this.navItems, function(v){
3725             
3726             if (v.isActive()) {
3727                 prev = v;
3728                 
3729             }
3730             
3731         });
3732         return prev;
3733     },
3734     
3735     indexOfNav : function()
3736     {
3737         
3738         var prev = false;
3739         Roo.each(this.navItems, function(v,i){
3740             
3741             if (v.isActive()) {
3742                 prev = i;
3743                 
3744             }
3745             
3746         });
3747         return prev;
3748     },
3749     /**
3750     * adds a Navigation item
3751     * @param {Roo.bootstrap.NavItem} the navitem to add
3752     */
3753     addItem : function(cfg)
3754     {
3755         var cn = new Roo.bootstrap.NavItem(cfg);
3756         this.register(cn);
3757         cn.parentId = this.id;
3758         cn.onRender(this.el, null);
3759         return cn;
3760     },
3761     /**
3762     * register a Navigation item
3763     * @param {Roo.bootstrap.NavItem} the navitem to add
3764     */
3765     register : function(item)
3766     {
3767         this.navItems.push( item);
3768         item.navId = this.navId;
3769     
3770     },
3771     
3772     /**
3773     * clear all the Navigation item
3774     */
3775    
3776     clearAll : function()
3777     {
3778         this.navItems = [];
3779         this.el.dom.innerHTML = '';
3780     },
3781     
3782     getNavItem: function(tabId)
3783     {
3784         var ret = false;
3785         Roo.each(this.navItems, function(e) {
3786             if (e.tabId == tabId) {
3787                ret =  e;
3788                return false;
3789             }
3790             return true;
3791             
3792         });
3793         return ret;
3794     },
3795     
3796     setActiveNext : function()
3797     {
3798         var i = this.indexOfNav(this.getActive());
3799         if (i > this.navItems.length) {
3800             return;
3801         }
3802         this.setActiveItem(this.navItems[i+1]);
3803     },
3804     setActivePrev : function()
3805     {
3806         var i = this.indexOfNav(this.getActive());
3807         if (i  < 1) {
3808             return;
3809         }
3810         this.setActiveItem(this.navItems[i-1]);
3811     },
3812     clearWasActive : function(except) {
3813         Roo.each(this.navItems, function(e) {
3814             if (e.tabId != except.tabId && e.was_active) {
3815                e.was_active = false;
3816                return false;
3817             }
3818             return true;
3819             
3820         });
3821     },
3822     getWasActive : function ()
3823     {
3824         var r = false;
3825         Roo.each(this.navItems, function(e) {
3826             if (e.was_active) {
3827                r = e;
3828                return false;
3829             }
3830             return true;
3831             
3832         });
3833         return r;
3834     }
3835     
3836     
3837 });
3838
3839  
3840 Roo.apply(Roo.bootstrap.NavGroup, {
3841     
3842     groups: {},
3843      /**
3844     * register a Navigation Group
3845     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3846     */
3847     register : function(navgrp)
3848     {
3849         this.groups[navgrp.navId] = navgrp;
3850         
3851     },
3852     /**
3853     * fetch a Navigation Group based on the navigation ID
3854     * @param {string} the navgroup to add
3855     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3856     */
3857     get: function(navId) {
3858         if (typeof(this.groups[navId]) == 'undefined') {
3859             return false;
3860             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3861         }
3862         return this.groups[navId] ;
3863     }
3864     
3865     
3866     
3867 });
3868
3869  /*
3870  * - LGPL
3871  *
3872  * row
3873  * 
3874  */
3875
3876 /**
3877  * @class Roo.bootstrap.NavItem
3878  * @extends Roo.bootstrap.Component
3879  * Bootstrap Navbar.NavItem class
3880  * @cfg {String} href  link to
3881  * @cfg {String} html content of button
3882  * @cfg {String} badge text inside badge
3883  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3884  * @cfg {String} glyphicon name of glyphicon
3885  * @cfg {String} icon name of font awesome icon
3886  * @cfg {Boolean} active Is item active
3887  * @cfg {Boolean} disabled Is item disabled
3888  
3889  * @cfg {Boolean} preventDefault (true | false) default false
3890  * @cfg {String} tabId the tab that this item activates.
3891  * @cfg {String} tagtype (a|span) render as a href or span?
3892  * @cfg {Boolean} animateRef (true|false) link to element default false  
3893   
3894  * @constructor
3895  * Create a new Navbar Item
3896  * @param {Object} config The config object
3897  */
3898 Roo.bootstrap.NavItem = function(config){
3899     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3900     this.addEvents({
3901         // raw events
3902         /**
3903          * @event click
3904          * The raw click event for the entire grid.
3905          * @param {Roo.EventObject} e
3906          */
3907         "click" : true,
3908          /**
3909             * @event changed
3910             * Fires when the active item active state changes
3911             * @param {Roo.bootstrap.NavItem} this
3912             * @param {boolean} state the new state
3913              
3914          */
3915         'changed': true,
3916         /**
3917             * @event scrollto
3918             * Fires when scroll to element
3919             * @param {Roo.bootstrap.NavItem} this
3920             * @param {Object} options
3921             * @param {Roo.EventObject} e
3922              
3923          */
3924         'scrollto': true
3925     });
3926    
3927 };
3928
3929 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3930     
3931     href: false,
3932     html: '',
3933     badge: '',
3934     icon: false,
3935     glyphicon: false,
3936     active: false,
3937     preventDefault : false,
3938     tabId : false,
3939     tagtype : 'a',
3940     disabled : false,
3941     animateRef : false,
3942     was_active : false,
3943     
3944     getAutoCreate : function(){
3945          
3946         var cfg = {
3947             tag: 'li',
3948             cls: 'nav-item'
3949             
3950         }
3951         if (this.active) {
3952             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3953         }
3954         if (this.disabled) {
3955             cfg.cls += ' disabled';
3956         }
3957         
3958         if (this.href || this.html || this.glyphicon || this.icon) {
3959             cfg.cn = [
3960                 {
3961                     tag: this.tagtype,
3962                     href : this.href || "#",
3963                     html: this.html || ''
3964                 }
3965             ];
3966             
3967             if (this.icon) {
3968                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3969             }
3970
3971             if(this.glyphicon) {
3972                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3973             }
3974             
3975             if (this.menu) {
3976                 
3977                 cfg.cn[0].html += " <span class='caret'></span>";
3978              
3979             }
3980             
3981             if (this.badge !== '') {
3982                  
3983                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3984             }
3985         }
3986         
3987         
3988         
3989         return cfg;
3990     },
3991     initEvents: function() 
3992     {
3993         if (typeof (this.menu) != 'undefined') {
3994             this.menu.parentType = this.xtype;
3995             this.menu.triggerEl = this.el;
3996             this.menu = this.addxtype(Roo.apply({}, this.menu));
3997         }
3998         
3999         this.el.select('a',true).on('click', this.onClick, this);
4000         
4001         if(this.tagtype == 'span'){
4002             this.el.select('span',true).on('click', this.onClick, this);
4003         }
4004        
4005         // at this point parent should be available..
4006         this.parent().register(this);
4007     },
4008     
4009     onClick : function(e)
4010     {
4011         if(
4012                 this.preventDefault || 
4013                 this.href == '#' 
4014         ){
4015             
4016             e.preventDefault();
4017         }
4018         
4019         if (this.disabled) {
4020             return;
4021         }
4022         
4023         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4024         if (tg && tg.transition) {
4025             Roo.log("waiting for the transitionend");
4026             return;
4027         }
4028         
4029         
4030         
4031         //Roo.log("fire event clicked");
4032         if(this.fireEvent('click', this, e) === false){
4033             return;
4034         };
4035         
4036         if(this.tagtype == 'span'){
4037             return;
4038         }
4039         
4040         //Roo.log(this.href);
4041         var ael = this.el.select('a',true).first();
4042         //Roo.log(ael);
4043         
4044         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4045             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4046             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4047                 return; // ignore... - it's a 'hash' to another page.
4048             }
4049             
4050             e.preventDefault();
4051             this.scrollToElement(e);
4052         }
4053         
4054         
4055         var p =  this.parent();
4056    
4057         if (['tabs','pills'].indexOf(p.type)!==-1) {
4058             if (typeof(p.setActiveItem) !== 'undefined') {
4059                 p.setActiveItem(this);
4060             }
4061         }
4062         
4063         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4064         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4065             // remove the collapsed menu expand...
4066             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4067         }
4068     },
4069     
4070     isActive: function () {
4071         return this.active
4072     },
4073     setActive : function(state, fire, is_was_active)
4074     {
4075         if (this.active && !state & this.navId) {
4076             this.was_active = true;
4077             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4078             if (nv) {
4079                 nv.clearWasActive(this);
4080             }
4081             
4082         }
4083         this.active = state;
4084         
4085         if (!state ) {
4086             this.el.removeClass('active');
4087         } else if (!this.el.hasClass('active')) {
4088             this.el.addClass('active');
4089         }
4090         if (fire) {
4091             this.fireEvent('changed', this, state);
4092         }
4093         
4094         // show a panel if it's registered and related..
4095         
4096         if (!this.navId || !this.tabId || !state || is_was_active) {
4097             return;
4098         }
4099         
4100         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4101         if (!tg) {
4102             return;
4103         }
4104         var pan = tg.getPanelByName(this.tabId);
4105         if (!pan) {
4106             return;
4107         }
4108         // if we can not flip to new panel - go back to old nav highlight..
4109         if (false == tg.showPanel(pan)) {
4110             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4111             if (nv) {
4112                 var onav = nv.getWasActive();
4113                 if (onav) {
4114                     onav.setActive(true, false, true);
4115                 }
4116             }
4117             
4118         }
4119         
4120         
4121         
4122     },
4123      // this should not be here...
4124     setDisabled : function(state)
4125     {
4126         this.disabled = state;
4127         if (!state ) {
4128             this.el.removeClass('disabled');
4129         } else if (!this.el.hasClass('disabled')) {
4130             this.el.addClass('disabled');
4131         }
4132         
4133     },
4134     
4135     /**
4136      * Fetch the element to display the tooltip on.
4137      * @return {Roo.Element} defaults to this.el
4138      */
4139     tooltipEl : function()
4140     {
4141         return this.el.select('' + this.tagtype + '', true).first();
4142     },
4143     
4144     scrollToElement : function(e)
4145     {
4146         var c = document.body;
4147         
4148         /*
4149          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4150          */
4151         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4152             c = document.documentElement;
4153         }
4154         
4155         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4156         
4157         if(!target){
4158             return;
4159         }
4160
4161         var o = target.calcOffsetsTo(c);
4162         
4163         var options = {
4164             target : target,
4165             value : o[1]
4166         }
4167         
4168         this.fireEvent('scrollto', this, options, e);
4169         
4170         Roo.get(c).scrollTo('top', options.value, true);
4171         
4172         return;
4173     }
4174 });
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * sidebar item
4181  *
4182  *  li
4183  *    <span> icon </span>
4184  *    <span> text </span>
4185  *    <span>badge </span>
4186  */
4187
4188 /**
4189  * @class Roo.bootstrap.NavSidebarItem
4190  * @extends Roo.bootstrap.NavItem
4191  * Bootstrap Navbar.NavSidebarItem class
4192  * @constructor
4193  * Create a new Navbar Button
4194  * @param {Object} config The config object
4195  */
4196 Roo.bootstrap.NavSidebarItem = function(config){
4197     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4198     this.addEvents({
4199         // raw events
4200         /**
4201          * @event click
4202          * The raw click event for the entire grid.
4203          * @param {Roo.EventObject} e
4204          */
4205         "click" : true,
4206          /**
4207             * @event changed
4208             * Fires when the active item active state changes
4209             * @param {Roo.bootstrap.NavSidebarItem} this
4210             * @param {boolean} state the new state
4211              
4212          */
4213         'changed': true
4214     });
4215    
4216 };
4217
4218 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4219     
4220     
4221     getAutoCreate : function(){
4222         
4223         
4224         var a = {
4225                 tag: 'a',
4226                 href : this.href || '#',
4227                 cls: '',
4228                 html : '',
4229                 cn : []
4230         };
4231         var cfg = {
4232             tag: 'li',
4233             cls: '',
4234             cn: [ a ]
4235         }
4236         var span = {
4237             tag: 'span',
4238             html : this.html || ''
4239         }
4240         
4241         
4242         if (this.active) {
4243             cfg.cls += ' active';
4244         }
4245         
4246         // left icon..
4247         if (this.glyphicon || this.icon) {
4248             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4249             a.cn.push({ tag : 'i', cls : c }) ;
4250         }
4251         // html..
4252         a.cn.push(span);
4253         // then badge..
4254         if (this.badge !== '') {
4255             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4256         }
4257         // fi
4258         if (this.menu) {
4259             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4260             a.cls += 'dropdown-toggle treeview' ;
4261             
4262         }
4263         
4264         
4265         
4266         return cfg;
4267          
4268            
4269     }
4270    
4271      
4272  
4273 });
4274  
4275
4276  /*
4277  * - LGPL
4278  *
4279  * row
4280  * 
4281  */
4282
4283 /**
4284  * @class Roo.bootstrap.Row
4285  * @extends Roo.bootstrap.Component
4286  * Bootstrap Row class (contains columns...)
4287  * 
4288  * @constructor
4289  * Create a new Row
4290  * @param {Object} config The config object
4291  */
4292
4293 Roo.bootstrap.Row = function(config){
4294     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4295 };
4296
4297 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4298     
4299     getAutoCreate : function(){
4300        return {
4301             cls: 'row clearfix'
4302        };
4303     }
4304     
4305     
4306 });
4307
4308  
4309
4310  /*
4311  * - LGPL
4312  *
4313  * element
4314  * 
4315  */
4316
4317 /**
4318  * @class Roo.bootstrap.Element
4319  * @extends Roo.bootstrap.Component
4320  * Bootstrap Element class
4321  * @cfg {String} html contents of the element
4322  * @cfg {String} tag tag of the element
4323  * @cfg {String} cls class of the element
4324  * @cfg {Boolean} preventDefault (true|false) default false
4325  * @cfg {Boolean} clickable (true|false) default false
4326  * 
4327  * @constructor
4328  * Create a new Element
4329  * @param {Object} config The config object
4330  */
4331
4332 Roo.bootstrap.Element = function(config){
4333     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4334     
4335     this.addEvents({
4336         // raw events
4337         /**
4338          * @event click
4339          * When a element is chick
4340          * @param {Roo.bootstrap.Element} this
4341          * @param {Roo.EventObject} e
4342          */
4343         "click" : true
4344     });
4345 };
4346
4347 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4348     
4349     tag: 'div',
4350     cls: '',
4351     html: '',
4352     preventDefault: false, 
4353     clickable: false,
4354     
4355     getAutoCreate : function(){
4356         
4357         var cfg = {
4358             tag: this.tag,
4359             cls: this.cls,
4360             html: this.html
4361         }
4362         
4363         return cfg;
4364     },
4365     
4366     initEvents: function() 
4367     {
4368         Roo.bootstrap.Element.superclass.initEvents.call(this);
4369         
4370         if(this.clickable){
4371             this.el.on('click', this.onClick, this);
4372         }
4373         
4374     },
4375     
4376     onClick : function(e)
4377     {
4378         if(this.preventDefault){
4379             e.preventDefault();
4380         }
4381         
4382         this.fireEvent('click', this, e);
4383     },
4384     
4385     getValue : function()
4386     {
4387         return this.el.dom.innerHTML;
4388     },
4389     
4390     setValue : function(value)
4391     {
4392         this.el.dom.innerHTML = value;
4393     }
4394    
4395 });
4396
4397  
4398
4399  /*
4400  * - LGPL
4401  *
4402  * pagination
4403  * 
4404  */
4405
4406 /**
4407  * @class Roo.bootstrap.Pagination
4408  * @extends Roo.bootstrap.Component
4409  * Bootstrap Pagination class
4410  * @cfg {String} size xs | sm | md | lg
4411  * @cfg {Boolean} inverse false | true
4412  * 
4413  * @constructor
4414  * Create a new Pagination
4415  * @param {Object} config The config object
4416  */
4417
4418 Roo.bootstrap.Pagination = function(config){
4419     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4420 };
4421
4422 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4423     
4424     cls: false,
4425     size: false,
4426     inverse: false,
4427     
4428     getAutoCreate : function(){
4429         var cfg = {
4430             tag: 'ul',
4431                 cls: 'pagination'
4432         };
4433         if (this.inverse) {
4434             cfg.cls += ' inverse';
4435         }
4436         if (this.html) {
4437             cfg.html=this.html;
4438         }
4439         if (this.cls) {
4440             cfg.cls += " " + this.cls;
4441         }
4442         return cfg;
4443     }
4444    
4445 });
4446
4447  
4448
4449  /*
4450  * - LGPL
4451  *
4452  * Pagination item
4453  * 
4454  */
4455
4456
4457 /**
4458  * @class Roo.bootstrap.PaginationItem
4459  * @extends Roo.bootstrap.Component
4460  * Bootstrap PaginationItem class
4461  * @cfg {String} html text
4462  * @cfg {String} href the link
4463  * @cfg {Boolean} preventDefault (true | false) default true
4464  * @cfg {Boolean} active (true | false) default false
4465  * @cfg {Boolean} disabled default false
4466  * 
4467  * 
4468  * @constructor
4469  * Create a new PaginationItem
4470  * @param {Object} config The config object
4471  */
4472
4473
4474 Roo.bootstrap.PaginationItem = function(config){
4475     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4476     this.addEvents({
4477         // raw events
4478         /**
4479          * @event click
4480          * The raw click event for the entire grid.
4481          * @param {Roo.EventObject} e
4482          */
4483         "click" : true
4484     });
4485 };
4486
4487 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4488     
4489     href : false,
4490     html : false,
4491     preventDefault: true,
4492     active : false,
4493     cls : false,
4494     disabled: false,
4495     
4496     getAutoCreate : function(){
4497         var cfg= {
4498             tag: 'li',
4499             cn: [
4500                 {
4501                     tag : 'a',
4502                     href : this.href ? this.href : '#',
4503                     html : this.html ? this.html : ''
4504                 }
4505             ]
4506         };
4507         
4508         if(this.cls){
4509             cfg.cls = this.cls;
4510         }
4511         
4512         if(this.disabled){
4513             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4514         }
4515         
4516         if(this.active){
4517             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4518         }
4519         
4520         return cfg;
4521     },
4522     
4523     initEvents: function() {
4524         
4525         this.el.on('click', this.onClick, this);
4526         
4527     },
4528     onClick : function(e)
4529     {
4530         Roo.log('PaginationItem on click ');
4531         if(this.preventDefault){
4532             e.preventDefault();
4533         }
4534         
4535         if(this.disabled){
4536             return;
4537         }
4538         
4539         this.fireEvent('click', this, e);
4540     }
4541    
4542 });
4543
4544  
4545
4546  /*
4547  * - LGPL
4548  *
4549  * slider
4550  * 
4551  */
4552
4553
4554 /**
4555  * @class Roo.bootstrap.Slider
4556  * @extends Roo.bootstrap.Component
4557  * Bootstrap Slider class
4558  *    
4559  * @constructor
4560  * Create a new Slider
4561  * @param {Object} config The config object
4562  */
4563
4564 Roo.bootstrap.Slider = function(config){
4565     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4566 };
4567
4568 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4569     
4570     getAutoCreate : function(){
4571         
4572         var cfg = {
4573             tag: 'div',
4574             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4575             cn: [
4576                 {
4577                     tag: 'a',
4578                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4579                 }
4580             ]
4581         }
4582         
4583         return cfg;
4584     }
4585    
4586 });
4587
4588  /*
4589  * Based on:
4590  * Ext JS Library 1.1.1
4591  * Copyright(c) 2006-2007, Ext JS, LLC.
4592  *
4593  * Originally Released Under LGPL - original licence link has changed is not relivant.
4594  *
4595  * Fork - LGPL
4596  * <script type="text/javascript">
4597  */
4598  
4599
4600 /**
4601  * @class Roo.grid.ColumnModel
4602  * @extends Roo.util.Observable
4603  * This is the default implementation of a ColumnModel used by the Grid. It defines
4604  * the columns in the grid.
4605  * <br>Usage:<br>
4606  <pre><code>
4607  var colModel = new Roo.grid.ColumnModel([
4608         {header: "Ticker", width: 60, sortable: true, locked: true},
4609         {header: "Company Name", width: 150, sortable: true},
4610         {header: "Market Cap.", width: 100, sortable: true},
4611         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4612         {header: "Employees", width: 100, sortable: true, resizable: false}
4613  ]);
4614  </code></pre>
4615  * <p>
4616  
4617  * The config options listed for this class are options which may appear in each
4618  * individual column definition.
4619  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4620  * @constructor
4621  * @param {Object} config An Array of column config objects. See this class's
4622  * config objects for details.
4623 */
4624 Roo.grid.ColumnModel = function(config){
4625         /**
4626      * The config passed into the constructor
4627      */
4628     this.config = config;
4629     this.lookup = {};
4630
4631     // if no id, create one
4632     // if the column does not have a dataIndex mapping,
4633     // map it to the order it is in the config
4634     for(var i = 0, len = config.length; i < len; i++){
4635         var c = config[i];
4636         if(typeof c.dataIndex == "undefined"){
4637             c.dataIndex = i;
4638         }
4639         if(typeof c.renderer == "string"){
4640             c.renderer = Roo.util.Format[c.renderer];
4641         }
4642         if(typeof c.id == "undefined"){
4643             c.id = Roo.id();
4644         }
4645         if(c.editor && c.editor.xtype){
4646             c.editor  = Roo.factory(c.editor, Roo.grid);
4647         }
4648         if(c.editor && c.editor.isFormField){
4649             c.editor = new Roo.grid.GridEditor(c.editor);
4650         }
4651         this.lookup[c.id] = c;
4652     }
4653
4654     /**
4655      * The width of columns which have no width specified (defaults to 100)
4656      * @type Number
4657      */
4658     this.defaultWidth = 100;
4659
4660     /**
4661      * Default sortable of columns which have no sortable specified (defaults to false)
4662      * @type Boolean
4663      */
4664     this.defaultSortable = false;
4665
4666     this.addEvents({
4667         /**
4668              * @event widthchange
4669              * Fires when the width of a column changes.
4670              * @param {ColumnModel} this
4671              * @param {Number} columnIndex The column index
4672              * @param {Number} newWidth The new width
4673              */
4674             "widthchange": true,
4675         /**
4676              * @event headerchange
4677              * Fires when the text of a header changes.
4678              * @param {ColumnModel} this
4679              * @param {Number} columnIndex The column index
4680              * @param {Number} newText The new header text
4681              */
4682             "headerchange": true,
4683         /**
4684              * @event hiddenchange
4685              * Fires when a column is hidden or "unhidden".
4686              * @param {ColumnModel} this
4687              * @param {Number} columnIndex The column index
4688              * @param {Boolean} hidden true if hidden, false otherwise
4689              */
4690             "hiddenchange": true,
4691             /**
4692          * @event columnmoved
4693          * Fires when a column is moved.
4694          * @param {ColumnModel} this
4695          * @param {Number} oldIndex
4696          * @param {Number} newIndex
4697          */
4698         "columnmoved" : true,
4699         /**
4700          * @event columlockchange
4701          * Fires when a column's locked state is changed
4702          * @param {ColumnModel} this
4703          * @param {Number} colIndex
4704          * @param {Boolean} locked true if locked
4705          */
4706         "columnlockchange" : true
4707     });
4708     Roo.grid.ColumnModel.superclass.constructor.call(this);
4709 };
4710 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4711     /**
4712      * @cfg {String} header The header text to display in the Grid view.
4713      */
4714     /**
4715      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4716      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4717      * specified, the column's index is used as an index into the Record's data Array.
4718      */
4719     /**
4720      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4721      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4722      */
4723     /**
4724      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4725      * Defaults to the value of the {@link #defaultSortable} property.
4726      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4727      */
4728     /**
4729      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4730      */
4731     /**
4732      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4733      */
4734     /**
4735      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4736      */
4737     /**
4738      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4739      */
4740     /**
4741      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4742      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4743      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4744      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4745      */
4746        /**
4747      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4748      */
4749     /**
4750      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4751      */
4752     /**
4753      * @cfg {String} cursor (Optional)
4754      */
4755     /**
4756      * @cfg {String} tooltip (Optional)
4757      */
4758     /**
4759      * Returns the id of the column at the specified index.
4760      * @param {Number} index The column index
4761      * @return {String} the id
4762      */
4763     getColumnId : function(index){
4764         return this.config[index].id;
4765     },
4766
4767     /**
4768      * Returns the column for a specified id.
4769      * @param {String} id The column id
4770      * @return {Object} the column
4771      */
4772     getColumnById : function(id){
4773         return this.lookup[id];
4774     },
4775
4776     
4777     /**
4778      * Returns the column for a specified dataIndex.
4779      * @param {String} dataIndex The column dataIndex
4780      * @return {Object|Boolean} the column or false if not found
4781      */
4782     getColumnByDataIndex: function(dataIndex){
4783         var index = this.findColumnIndex(dataIndex);
4784         return index > -1 ? this.config[index] : false;
4785     },
4786     
4787     /**
4788      * Returns the index for a specified column id.
4789      * @param {String} id The column id
4790      * @return {Number} the index, or -1 if not found
4791      */
4792     getIndexById : function(id){
4793         for(var i = 0, len = this.config.length; i < len; i++){
4794             if(this.config[i].id == id){
4795                 return i;
4796             }
4797         }
4798         return -1;
4799     },
4800     
4801     /**
4802      * Returns the index for a specified column dataIndex.
4803      * @param {String} dataIndex The column dataIndex
4804      * @return {Number} the index, or -1 if not found
4805      */
4806     
4807     findColumnIndex : function(dataIndex){
4808         for(var i = 0, len = this.config.length; i < len; i++){
4809             if(this.config[i].dataIndex == dataIndex){
4810                 return i;
4811             }
4812         }
4813         return -1;
4814     },
4815     
4816     
4817     moveColumn : function(oldIndex, newIndex){
4818         var c = this.config[oldIndex];
4819         this.config.splice(oldIndex, 1);
4820         this.config.splice(newIndex, 0, c);
4821         this.dataMap = null;
4822         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4823     },
4824
4825     isLocked : function(colIndex){
4826         return this.config[colIndex].locked === true;
4827     },
4828
4829     setLocked : function(colIndex, value, suppressEvent){
4830         if(this.isLocked(colIndex) == value){
4831             return;
4832         }
4833         this.config[colIndex].locked = value;
4834         if(!suppressEvent){
4835             this.fireEvent("columnlockchange", this, colIndex, value);
4836         }
4837     },
4838
4839     getTotalLockedWidth : function(){
4840         var totalWidth = 0;
4841         for(var i = 0; i < this.config.length; i++){
4842             if(this.isLocked(i) && !this.isHidden(i)){
4843                 this.totalWidth += this.getColumnWidth(i);
4844             }
4845         }
4846         return totalWidth;
4847     },
4848
4849     getLockedCount : function(){
4850         for(var i = 0, len = this.config.length; i < len; i++){
4851             if(!this.isLocked(i)){
4852                 return i;
4853             }
4854         }
4855     },
4856
4857     /**
4858      * Returns the number of columns.
4859      * @return {Number}
4860      */
4861     getColumnCount : function(visibleOnly){
4862         if(visibleOnly === true){
4863             var c = 0;
4864             for(var i = 0, len = this.config.length; i < len; i++){
4865                 if(!this.isHidden(i)){
4866                     c++;
4867                 }
4868             }
4869             return c;
4870         }
4871         return this.config.length;
4872     },
4873
4874     /**
4875      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4876      * @param {Function} fn
4877      * @param {Object} scope (optional)
4878      * @return {Array} result
4879      */
4880     getColumnsBy : function(fn, scope){
4881         var r = [];
4882         for(var i = 0, len = this.config.length; i < len; i++){
4883             var c = this.config[i];
4884             if(fn.call(scope||this, c, i) === true){
4885                 r[r.length] = c;
4886             }
4887         }
4888         return r;
4889     },
4890
4891     /**
4892      * Returns true if the specified column is sortable.
4893      * @param {Number} col The column index
4894      * @return {Boolean}
4895      */
4896     isSortable : function(col){
4897         if(typeof this.config[col].sortable == "undefined"){
4898             return this.defaultSortable;
4899         }
4900         return this.config[col].sortable;
4901     },
4902
4903     /**
4904      * Returns the rendering (formatting) function defined for the column.
4905      * @param {Number} col The column index.
4906      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4907      */
4908     getRenderer : function(col){
4909         if(!this.config[col].renderer){
4910             return Roo.grid.ColumnModel.defaultRenderer;
4911         }
4912         return this.config[col].renderer;
4913     },
4914
4915     /**
4916      * Sets the rendering (formatting) function for a column.
4917      * @param {Number} col The column index
4918      * @param {Function} fn The function to use to process the cell's raw data
4919      * to return HTML markup for the grid view. The render function is called with
4920      * the following parameters:<ul>
4921      * <li>Data value.</li>
4922      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4923      * <li>css A CSS style string to apply to the table cell.</li>
4924      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4925      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4926      * <li>Row index</li>
4927      * <li>Column index</li>
4928      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4929      */
4930     setRenderer : function(col, fn){
4931         this.config[col].renderer = fn;
4932     },
4933
4934     /**
4935      * Returns the width for the specified column.
4936      * @param {Number} col The column index
4937      * @return {Number}
4938      */
4939     getColumnWidth : function(col){
4940         return this.config[col].width * 1 || this.defaultWidth;
4941     },
4942
4943     /**
4944      * Sets the width for a column.
4945      * @param {Number} col The column index
4946      * @param {Number} width The new width
4947      */
4948     setColumnWidth : function(col, width, suppressEvent){
4949         this.config[col].width = width;
4950         this.totalWidth = null;
4951         if(!suppressEvent){
4952              this.fireEvent("widthchange", this, col, width);
4953         }
4954     },
4955
4956     /**
4957      * Returns the total width of all columns.
4958      * @param {Boolean} includeHidden True to include hidden column widths
4959      * @return {Number}
4960      */
4961     getTotalWidth : function(includeHidden){
4962         if(!this.totalWidth){
4963             this.totalWidth = 0;
4964             for(var i = 0, len = this.config.length; i < len; i++){
4965                 if(includeHidden || !this.isHidden(i)){
4966                     this.totalWidth += this.getColumnWidth(i);
4967                 }
4968             }
4969         }
4970         return this.totalWidth;
4971     },
4972
4973     /**
4974      * Returns the header for the specified column.
4975      * @param {Number} col The column index
4976      * @return {String}
4977      */
4978     getColumnHeader : function(col){
4979         return this.config[col].header;
4980     },
4981
4982     /**
4983      * Sets the header for a column.
4984      * @param {Number} col The column index
4985      * @param {String} header The new header
4986      */
4987     setColumnHeader : function(col, header){
4988         this.config[col].header = header;
4989         this.fireEvent("headerchange", this, col, header);
4990     },
4991
4992     /**
4993      * Returns the tooltip for the specified column.
4994      * @param {Number} col The column index
4995      * @return {String}
4996      */
4997     getColumnTooltip : function(col){
4998             return this.config[col].tooltip;
4999     },
5000     /**
5001      * Sets the tooltip for a column.
5002      * @param {Number} col The column index
5003      * @param {String} tooltip The new tooltip
5004      */
5005     setColumnTooltip : function(col, tooltip){
5006             this.config[col].tooltip = tooltip;
5007     },
5008
5009     /**
5010      * Returns the dataIndex for the specified column.
5011      * @param {Number} col The column index
5012      * @return {Number}
5013      */
5014     getDataIndex : function(col){
5015         return this.config[col].dataIndex;
5016     },
5017
5018     /**
5019      * Sets the dataIndex for a column.
5020      * @param {Number} col The column index
5021      * @param {Number} dataIndex The new dataIndex
5022      */
5023     setDataIndex : function(col, dataIndex){
5024         this.config[col].dataIndex = dataIndex;
5025     },
5026
5027     
5028     
5029     /**
5030      * Returns true if the cell is editable.
5031      * @param {Number} colIndex The column index
5032      * @param {Number} rowIndex The row index
5033      * @return {Boolean}
5034      */
5035     isCellEditable : function(colIndex, rowIndex){
5036         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5037     },
5038
5039     /**
5040      * Returns the editor defined for the cell/column.
5041      * return false or null to disable editing.
5042      * @param {Number} colIndex The column index
5043      * @param {Number} rowIndex The row index
5044      * @return {Object}
5045      */
5046     getCellEditor : function(colIndex, rowIndex){
5047         return this.config[colIndex].editor;
5048     },
5049
5050     /**
5051      * Sets if a column is editable.
5052      * @param {Number} col The column index
5053      * @param {Boolean} editable True if the column is editable
5054      */
5055     setEditable : function(col, editable){
5056         this.config[col].editable = editable;
5057     },
5058
5059
5060     /**
5061      * Returns true if the column is hidden.
5062      * @param {Number} colIndex The column index
5063      * @return {Boolean}
5064      */
5065     isHidden : function(colIndex){
5066         return this.config[colIndex].hidden;
5067     },
5068
5069
5070     /**
5071      * Returns true if the column width cannot be changed
5072      */
5073     isFixed : function(colIndex){
5074         return this.config[colIndex].fixed;
5075     },
5076
5077     /**
5078      * Returns true if the column can be resized
5079      * @return {Boolean}
5080      */
5081     isResizable : function(colIndex){
5082         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5083     },
5084     /**
5085      * Sets if a column is hidden.
5086      * @param {Number} colIndex The column index
5087      * @param {Boolean} hidden True if the column is hidden
5088      */
5089     setHidden : function(colIndex, hidden){
5090         this.config[colIndex].hidden = hidden;
5091         this.totalWidth = null;
5092         this.fireEvent("hiddenchange", this, colIndex, hidden);
5093     },
5094
5095     /**
5096      * Sets the editor for a column.
5097      * @param {Number} col The column index
5098      * @param {Object} editor The editor object
5099      */
5100     setEditor : function(col, editor){
5101         this.config[col].editor = editor;
5102     }
5103 });
5104
5105 Roo.grid.ColumnModel.defaultRenderer = function(value){
5106         if(typeof value == "string" && value.length < 1){
5107             return "&#160;";
5108         }
5109         return value;
5110 };
5111
5112 // Alias for backwards compatibility
5113 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5114 /*
5115  * Based on:
5116  * Ext JS Library 1.1.1
5117  * Copyright(c) 2006-2007, Ext JS, LLC.
5118  *
5119  * Originally Released Under LGPL - original licence link has changed is not relivant.
5120  *
5121  * Fork - LGPL
5122  * <script type="text/javascript">
5123  */
5124  
5125 /**
5126  * @class Roo.LoadMask
5127  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5128  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5129  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5130  * element's UpdateManager load indicator and will be destroyed after the initial load.
5131  * @constructor
5132  * Create a new LoadMask
5133  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5134  * @param {Object} config The config object
5135  */
5136 Roo.LoadMask = function(el, config){
5137     this.el = Roo.get(el);
5138     Roo.apply(this, config);
5139     if(this.store){
5140         this.store.on('beforeload', this.onBeforeLoad, this);
5141         this.store.on('load', this.onLoad, this);
5142         this.store.on('loadexception', this.onLoadException, this);
5143         this.removeMask = false;
5144     }else{
5145         var um = this.el.getUpdateManager();
5146         um.showLoadIndicator = false; // disable the default indicator
5147         um.on('beforeupdate', this.onBeforeLoad, this);
5148         um.on('update', this.onLoad, this);
5149         um.on('failure', this.onLoad, this);
5150         this.removeMask = true;
5151     }
5152 };
5153
5154 Roo.LoadMask.prototype = {
5155     /**
5156      * @cfg {Boolean} removeMask
5157      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5158      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5159      */
5160     /**
5161      * @cfg {String} msg
5162      * The text to display in a centered loading message box (defaults to 'Loading...')
5163      */
5164     msg : 'Loading...',
5165     /**
5166      * @cfg {String} msgCls
5167      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5168      */
5169     msgCls : 'x-mask-loading',
5170
5171     /**
5172      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5173      * @type Boolean
5174      */
5175     disabled: false,
5176
5177     /**
5178      * Disables the mask to prevent it from being displayed
5179      */
5180     disable : function(){
5181        this.disabled = true;
5182     },
5183
5184     /**
5185      * Enables the mask so that it can be displayed
5186      */
5187     enable : function(){
5188         this.disabled = false;
5189     },
5190     
5191     onLoadException : function()
5192     {
5193         Roo.log(arguments);
5194         
5195         if (typeof(arguments[3]) != 'undefined') {
5196             Roo.MessageBox.alert("Error loading",arguments[3]);
5197         } 
5198         /*
5199         try {
5200             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5201                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5202             }   
5203         } catch(e) {
5204             
5205         }
5206         */
5207     
5208         
5209         
5210         this.el.unmask(this.removeMask);
5211     },
5212     // private
5213     onLoad : function()
5214     {
5215         this.el.unmask(this.removeMask);
5216     },
5217
5218     // private
5219     onBeforeLoad : function(){
5220         if(!this.disabled){
5221             this.el.mask(this.msg, this.msgCls);
5222         }
5223     },
5224
5225     // private
5226     destroy : function(){
5227         if(this.store){
5228             this.store.un('beforeload', this.onBeforeLoad, this);
5229             this.store.un('load', this.onLoad, this);
5230             this.store.un('loadexception', this.onLoadException, this);
5231         }else{
5232             var um = this.el.getUpdateManager();
5233             um.un('beforeupdate', this.onBeforeLoad, this);
5234             um.un('update', this.onLoad, this);
5235             um.un('failure', this.onLoad, this);
5236         }
5237     }
5238 };/*
5239  * - LGPL
5240  *
5241  * table
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Table
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Table class
5249  * @cfg {String} cls table class
5250  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5251  * @cfg {String} bgcolor Specifies the background color for a table
5252  * @cfg {Number} border Specifies whether the table cells should have borders or not
5253  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5254  * @cfg {Number} cellspacing Specifies the space between cells
5255  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5256  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5257  * @cfg {String} sortable Specifies that the table should be sortable
5258  * @cfg {String} summary Specifies a summary of the content of a table
5259  * @cfg {Number} width Specifies the width of a table
5260  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5261  * 
5262  * @cfg {boolean} striped Should the rows be alternative striped
5263  * @cfg {boolean} bordered Add borders to the table
5264  * @cfg {boolean} hover Add hover highlighting
5265  * @cfg {boolean} condensed Format condensed
5266  * @cfg {boolean} responsive Format condensed
5267  * @cfg {Boolean} loadMask (true|false) default false
5268  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5269  * @cfg {Boolean} thead (true|false) generate thead, default true
5270  * @cfg {Boolean} RowSelection (true|false) default false
5271  * @cfg {Boolean} CellSelection (true|false) default false
5272  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5273  
5274  * 
5275  * @constructor
5276  * Create a new Table
5277  * @param {Object} config The config object
5278  */
5279
5280 Roo.bootstrap.Table = function(config){
5281     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5282     
5283     if (this.sm) {
5284         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5285         this.sm = this.selModel;
5286         this.sm.xmodule = this.xmodule || false;
5287     }
5288     if (this.cm && typeof(this.cm.config) == 'undefined') {
5289         this.colModel = new Roo.grid.ColumnModel(this.cm);
5290         this.cm = this.colModel;
5291         this.cm.xmodule = this.xmodule || false;
5292     }
5293     if (this.store) {
5294         this.store= Roo.factory(this.store, Roo.data);
5295         this.ds = this.store;
5296         this.ds.xmodule = this.xmodule || false;
5297          
5298     }
5299     if (this.footer && this.store) {
5300         this.footer.dataSource = this.ds;
5301         this.footer = Roo.factory(this.footer);
5302     }
5303     
5304     /** @private */
5305     this.addEvents({
5306         /**
5307          * @event cellclick
5308          * Fires when a cell is clicked
5309          * @param {Roo.bootstrap.Table} this
5310          * @param {Roo.Element} el
5311          * @param {Number} rowIndex
5312          * @param {Number} columnIndex
5313          * @param {Roo.EventObject} e
5314          */
5315         "cellclick" : true,
5316         /**
5317          * @event celldblclick
5318          * Fires when a cell is double clicked
5319          * @param {Roo.bootstrap.Table} this
5320          * @param {Roo.Element} el
5321          * @param {Number} rowIndex
5322          * @param {Number} columnIndex
5323          * @param {Roo.EventObject} e
5324          */
5325         "celldblclick" : true,
5326         /**
5327          * @event rowclick
5328          * Fires when a row is clicked
5329          * @param {Roo.bootstrap.Table} this
5330          * @param {Roo.Element} el
5331          * @param {Number} rowIndex
5332          * @param {Roo.EventObject} e
5333          */
5334         "rowclick" : true,
5335         /**
5336          * @event rowdblclick
5337          * Fires when a row is double clicked
5338          * @param {Roo.bootstrap.Table} this
5339          * @param {Roo.Element} el
5340          * @param {Number} rowIndex
5341          * @param {Roo.EventObject} e
5342          */
5343         "rowdblclick" : true,
5344         /**
5345          * @event mouseover
5346          * Fires when a mouseover occur
5347          * @param {Roo.bootstrap.Table} this
5348          * @param {Roo.Element} el
5349          * @param {Number} rowIndex
5350          * @param {Number} columnIndex
5351          * @param {Roo.EventObject} e
5352          */
5353         "mouseover" : true,
5354         /**
5355          * @event mouseout
5356          * Fires when a mouseout occur
5357          * @param {Roo.bootstrap.Table} this
5358          * @param {Roo.Element} el
5359          * @param {Number} rowIndex
5360          * @param {Number} columnIndex
5361          * @param {Roo.EventObject} e
5362          */
5363         "mouseout" : true,
5364         /**
5365          * @event rowclass
5366          * Fires when a row is rendered, so you can change add a style to it.
5367          * @param {Roo.bootstrap.Table} this
5368          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5369          */
5370         'rowclass' : true,
5371           /**
5372          * @event rowsrendered
5373          * Fires when all the  rows have been rendered
5374          * @param {Roo.bootstrap.Table} this
5375          */
5376         'rowsrendered' : true
5377         
5378     });
5379 };
5380
5381 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5382     
5383     cls: false,
5384     align: false,
5385     bgcolor: false,
5386     border: false,
5387     cellpadding: false,
5388     cellspacing: false,
5389     frame: false,
5390     rules: false,
5391     sortable: false,
5392     summary: false,
5393     width: false,
5394     striped : false,
5395     bordered: false,
5396     hover:  false,
5397     condensed : false,
5398     responsive : false,
5399     sm : false,
5400     cm : false,
5401     store : false,
5402     loadMask : false,
5403     tfoot : true,
5404     thead : true,
5405     RowSelection : false,
5406     CellSelection : false,
5407     layout : false,
5408     
5409     // Roo.Element - the tbody
5410     mainBody: false, 
5411     
5412     getAutoCreate : function(){
5413         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5414         
5415         cfg = {
5416             tag: 'table',
5417             cls : 'table',
5418             cn : []
5419         }
5420             
5421         if (this.striped) {
5422             cfg.cls += ' table-striped';
5423         }
5424         
5425         if (this.hover) {
5426             cfg.cls += ' table-hover';
5427         }
5428         if (this.bordered) {
5429             cfg.cls += ' table-bordered';
5430         }
5431         if (this.condensed) {
5432             cfg.cls += ' table-condensed';
5433         }
5434         if (this.responsive) {
5435             cfg.cls += ' table-responsive';
5436         }
5437         
5438         if (this.cls) {
5439             cfg.cls+=  ' ' +this.cls;
5440         }
5441         
5442         // this lot should be simplifed...
5443         
5444         if (this.align) {
5445             cfg.align=this.align;
5446         }
5447         if (this.bgcolor) {
5448             cfg.bgcolor=this.bgcolor;
5449         }
5450         if (this.border) {
5451             cfg.border=this.border;
5452         }
5453         if (this.cellpadding) {
5454             cfg.cellpadding=this.cellpadding;
5455         }
5456         if (this.cellspacing) {
5457             cfg.cellspacing=this.cellspacing;
5458         }
5459         if (this.frame) {
5460             cfg.frame=this.frame;
5461         }
5462         if (this.rules) {
5463             cfg.rules=this.rules;
5464         }
5465         if (this.sortable) {
5466             cfg.sortable=this.sortable;
5467         }
5468         if (this.summary) {
5469             cfg.summary=this.summary;
5470         }
5471         if (this.width) {
5472             cfg.width=this.width;
5473         }
5474         if (this.layout) {
5475             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5476         }
5477         
5478         if(this.store || this.cm){
5479             if(this.thead){
5480                 cfg.cn.push(this.renderHeader());
5481             }
5482             
5483             cfg.cn.push(this.renderBody());
5484             
5485             if(this.tfoot){
5486                 cfg.cn.push(this.renderFooter());
5487             }
5488             
5489             cfg.cls+=  ' TableGrid';
5490         }
5491         
5492         return { cn : [ cfg ] };
5493     },
5494     
5495     initEvents : function()
5496     {   
5497         if(!this.store || !this.cm){
5498             return;
5499         }
5500         
5501         //Roo.log('initEvents with ds!!!!');
5502         
5503         this.mainBody = this.el.select('tbody', true).first();
5504         
5505         
5506         var _this = this;
5507         
5508         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5509             e.on('click', _this.sort, _this);
5510         });
5511         
5512         this.el.on("click", this.onClick, this);
5513         this.el.on("dblclick", this.onDblClick, this);
5514         
5515         // why is this done????? = it breaks dialogs??
5516         //this.parent().el.setStyle('position', 'relative');
5517         
5518         
5519         if (this.footer) {
5520             this.footer.parentId = this.id;
5521             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5522         }
5523         
5524         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5525         
5526         this.store.on('load', this.onLoad, this);
5527         this.store.on('beforeload', this.onBeforeLoad, this);
5528         this.store.on('update', this.onUpdate, this);
5529         this.store.on('add', this.onAdd, this);
5530         
5531     },
5532     
5533     onMouseover : function(e, el)
5534     {
5535         var cell = Roo.get(el);
5536         
5537         if(!cell){
5538             return;
5539         }
5540         
5541         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5542             cell = cell.findParent('td', false, true);
5543         }
5544         
5545         var row = cell.findParent('tr', false, true);
5546         var cellIndex = cell.dom.cellIndex;
5547         var rowIndex = row.dom.rowIndex - 1; // start from 0
5548         
5549         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5550         
5551     },
5552     
5553     onMouseout : function(e, el)
5554     {
5555         var cell = Roo.get(el);
5556         
5557         if(!cell){
5558             return;
5559         }
5560         
5561         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5562             cell = cell.findParent('td', false, true);
5563         }
5564         
5565         var row = cell.findParent('tr', false, true);
5566         var cellIndex = cell.dom.cellIndex;
5567         var rowIndex = row.dom.rowIndex - 1; // start from 0
5568         
5569         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5570         
5571     },
5572     
5573     onClick : function(e, el)
5574     {
5575         var cell = Roo.get(el);
5576         
5577         if(!cell || (!this.CellSelection && !this.RowSelection)){
5578             return;
5579         }
5580         
5581         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5582             cell = cell.findParent('td', false, true);
5583         }
5584         
5585         if(!cell || typeof(cell) == 'undefined'){
5586             return;
5587         }
5588         
5589         var row = cell.findParent('tr', false, true);
5590         
5591         if(!row || typeof(row) == 'undefined'){
5592             return;
5593         }
5594         
5595         var cellIndex = cell.dom.cellIndex;
5596         var rowIndex = this.getRowIndex(row);
5597         
5598         if(this.CellSelection){
5599             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5600         }
5601         
5602         if(this.RowSelection){
5603             this.fireEvent('rowclick', this, row, rowIndex, e);
5604         }
5605         
5606         
5607     },
5608     
5609     onDblClick : function(e,el)
5610     {
5611         var cell = Roo.get(el);
5612         
5613         if(!cell || (!this.CellSelection && !this.RowSelection)){
5614             return;
5615         }
5616         
5617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5618             cell = cell.findParent('td', false, true);
5619         }
5620         
5621         if(!cell || typeof(cell) == 'undefined'){
5622             return;
5623         }
5624         
5625         var row = cell.findParent('tr', false, true);
5626         
5627         if(!row || typeof(row) == 'undefined'){
5628             return;
5629         }
5630         
5631         var cellIndex = cell.dom.cellIndex;
5632         var rowIndex = this.getRowIndex(row);
5633         
5634         if(this.CellSelection){
5635             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5636         }
5637         
5638         if(this.RowSelection){
5639             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5640         }
5641     },
5642     
5643     sort : function(e,el)
5644     {
5645         var col = Roo.get(el);
5646         
5647         if(!col.hasClass('sortable')){
5648             return;
5649         }
5650         
5651         var sort = col.attr('sort');
5652         var dir = 'ASC';
5653         
5654         if(col.hasClass('glyphicon-arrow-up')){
5655             dir = 'DESC';
5656         }
5657         
5658         this.store.sortInfo = {field : sort, direction : dir};
5659         
5660         if (this.footer) {
5661             Roo.log("calling footer first");
5662             this.footer.onClick('first');
5663         } else {
5664         
5665             this.store.load({ params : { start : 0 } });
5666         }
5667     },
5668     
5669     renderHeader : function()
5670     {
5671         var header = {
5672             tag: 'thead',
5673             cn : []
5674         };
5675         
5676         var cm = this.cm;
5677         
5678         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5679             
5680             var config = cm.config[i];
5681                     
5682             var c = {
5683                 tag: 'th',
5684                 style : '',
5685                 html: cm.getColumnHeader(i)
5686             };
5687             
5688             if(typeof(config.tooltip) != 'undefined'){
5689                 c.tooltip = config.tooltip;
5690             }
5691             
5692             if(typeof(config.colspan) != 'undefined'){
5693                 c.colspan = config.colspan;
5694             }
5695             
5696             if(typeof(config.hidden) != 'undefined' && config.hidden){
5697                 c.style += ' display:none;';
5698             }
5699             
5700             if(typeof(config.dataIndex) != 'undefined'){
5701                 c.sort = config.dataIndex;
5702             }
5703             
5704             if(typeof(config.sortable) != 'undefined' && config.sortable){
5705                 c.cls = 'sortable';
5706             }
5707             
5708             if(typeof(config.align) != 'undefined' && config.align.length){
5709                 c.style += ' text-align:' + config.align + ';';
5710             }
5711             
5712             if(typeof(config.width) != 'undefined'){
5713                 c.style += ' width:' + config.width + 'px;';
5714             }
5715             
5716             if(typeof(config.cls) != 'undefined'){
5717                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5718             }
5719             
5720             header.cn.push(c)
5721         }
5722         
5723         return header;
5724     },
5725     
5726     renderBody : function()
5727     {
5728         var body = {
5729             tag: 'tbody',
5730             cn : [
5731                 {
5732                     tag: 'tr',
5733                     cn : [
5734                         {
5735                             tag : 'td',
5736                             colspan :  this.cm.getColumnCount()
5737                         }
5738                     ]
5739                 }
5740             ]
5741         };
5742         
5743         return body;
5744     },
5745     
5746     renderFooter : function()
5747     {
5748         var footer = {
5749             tag: 'tfoot',
5750             cn : [
5751                 {
5752                     tag: 'tr',
5753                     cn : [
5754                         {
5755                             tag : 'td',
5756                             colspan :  this.cm.getColumnCount()
5757                         }
5758                     ]
5759                 }
5760             ]
5761         };
5762         
5763         return footer;
5764     },
5765     
5766     
5767     
5768     onLoad : function()
5769     {
5770         Roo.log('ds onload');
5771         this.clear();
5772         
5773         var _this = this;
5774         var cm = this.cm;
5775         var ds = this.store;
5776         
5777         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5778             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5779             
5780             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5781                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5782             }
5783             
5784             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5785                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5786             }
5787         });
5788         
5789         var tbody =  this.mainBody;
5790               
5791         if(ds.getCount() > 0){
5792             ds.data.each(function(d,rowIndex){
5793                 var row =  this.renderRow(cm, ds, rowIndex);
5794                 
5795                 tbody.createChild(row);
5796                 
5797                 var _this = this;
5798                 
5799                 if(row.cellObjects.length){
5800                     Roo.each(row.cellObjects, function(r){
5801                         _this.renderCellObject(r);
5802                     })
5803                 }
5804                 
5805             }, this);
5806         }
5807         
5808         Roo.each(this.el.select('tbody td', true).elements, function(e){
5809             e.on('mouseover', _this.onMouseover, _this);
5810         });
5811         
5812         Roo.each(this.el.select('tbody td', true).elements, function(e){
5813             e.on('mouseout', _this.onMouseout, _this);
5814         });
5815         this.fireEvent('rowsrendered', this);
5816         //if(this.loadMask){
5817         //    this.maskEl.hide();
5818         //}
5819     },
5820     
5821     
5822     onUpdate : function(ds,record)
5823     {
5824         this.refreshRow(record);
5825     },
5826     
5827     onRemove : function(ds, record, index, isUpdate){
5828         if(isUpdate !== true){
5829             this.fireEvent("beforerowremoved", this, index, record);
5830         }
5831         var bt = this.mainBody.dom;
5832         
5833         var rows = this.el.select('tbody > tr', true).elements;
5834         
5835         if(typeof(rows[index]) != 'undefined'){
5836             bt.removeChild(rows[index].dom);
5837         }
5838         
5839 //        if(bt.rows[index]){
5840 //            bt.removeChild(bt.rows[index]);
5841 //        }
5842         
5843         if(isUpdate !== true){
5844             //this.stripeRows(index);
5845             //this.syncRowHeights(index, index);
5846             //this.layout();
5847             this.fireEvent("rowremoved", this, index, record);
5848         }
5849     },
5850     
5851     onAdd : function(ds, records, rowIndex)
5852     {
5853         //Roo.log('on Add called');
5854         // - note this does not handle multiple adding very well..
5855         var bt = this.mainBody.dom;
5856         for (var i =0 ; i < records.length;i++) {
5857             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5858             //Roo.log(records[i]);
5859             //Roo.log(this.store.getAt(rowIndex+i));
5860             this.insertRow(this.store, rowIndex + i, false);
5861             return;
5862         }
5863         
5864     },
5865     
5866     
5867     refreshRow : function(record){
5868         var ds = this.store, index;
5869         if(typeof record == 'number'){
5870             index = record;
5871             record = ds.getAt(index);
5872         }else{
5873             index = ds.indexOf(record);
5874         }
5875         this.insertRow(ds, index, true);
5876         this.onRemove(ds, record, index+1, true);
5877         //this.syncRowHeights(index, index);
5878         //this.layout();
5879         this.fireEvent("rowupdated", this, index, record);
5880     },
5881     
5882     insertRow : function(dm, rowIndex, isUpdate){
5883         
5884         if(!isUpdate){
5885             this.fireEvent("beforerowsinserted", this, rowIndex);
5886         }
5887             //var s = this.getScrollState();
5888         var row = this.renderRow(this.cm, this.store, rowIndex);
5889         // insert before rowIndex..
5890         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5891         
5892         var _this = this;
5893                 
5894         if(row.cellObjects.length){
5895             Roo.each(row.cellObjects, function(r){
5896                 _this.renderCellObject(r);
5897             })
5898         }
5899             
5900         if(!isUpdate){
5901             this.fireEvent("rowsinserted", this, rowIndex);
5902             //this.syncRowHeights(firstRow, lastRow);
5903             //this.stripeRows(firstRow);
5904             //this.layout();
5905         }
5906         
5907     },
5908     
5909     
5910     getRowDom : function(rowIndex)
5911     {
5912         var rows = this.el.select('tbody > tr', true).elements;
5913         
5914         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5915         
5916     },
5917     // returns the object tree for a tr..
5918   
5919     
5920     renderRow : function(cm, ds, rowIndex) 
5921     {
5922         
5923         var d = ds.getAt(rowIndex);
5924         
5925         var row = {
5926             tag : 'tr',
5927             cn : []
5928         };
5929             
5930         var cellObjects = [];
5931         
5932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5933             var config = cm.config[i];
5934             
5935             var renderer = cm.getRenderer(i);
5936             var value = '';
5937             var id = false;
5938             
5939             if(typeof(renderer) !== 'undefined'){
5940                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5941             }
5942             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5943             // and are rendered into the cells after the row is rendered - using the id for the element.
5944             
5945             if(typeof(value) === 'object'){
5946                 id = Roo.id();
5947                 cellObjects.push({
5948                     container : id,
5949                     cfg : value 
5950                 })
5951             }
5952             
5953             var rowcfg = {
5954                 record: d,
5955                 rowIndex : rowIndex,
5956                 colIndex : i,
5957                 rowClass : ''
5958             }
5959
5960             this.fireEvent('rowclass', this, rowcfg);
5961             
5962             var td = {
5963                 tag: 'td',
5964                 cls : rowcfg.rowClass,
5965                 style: '',
5966                 html: (typeof(value) === 'object') ? '' : value
5967             };
5968             
5969             if (id) {
5970                 td.id = id;
5971             }
5972             
5973             if(typeof(config.colspan) != 'undefined'){
5974                 td.colspan = config.colspan;
5975             }
5976             
5977             if(typeof(config.hidden) != 'undefined' && config.hidden){
5978                 td.style += ' display:none;';
5979             }
5980             
5981             if(typeof(config.align) != 'undefined' && config.align.length){
5982                 td.style += ' text-align:' + config.align + ';';
5983             }
5984             
5985             if(typeof(config.width) != 'undefined'){
5986                 td.style += ' width:' +  config.width + 'px;';
5987             }
5988             
5989             if(typeof(config.cursor) != 'undefined'){
5990                 td.style += ' cursor:' +  config.cursor + ';';
5991             }
5992             
5993             if(typeof(config.cls) != 'undefined'){
5994                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5995             }
5996              
5997             row.cn.push(td);
5998            
5999         }
6000         
6001         row.cellObjects = cellObjects;
6002         
6003         return row;
6004           
6005     },
6006     
6007     
6008     
6009     onBeforeLoad : function()
6010     {
6011         //Roo.log('ds onBeforeLoad');
6012         
6013         //this.clear();
6014         
6015         //if(this.loadMask){
6016         //    this.maskEl.show();
6017         //}
6018     },
6019      /**
6020      * Remove all rows
6021      */
6022     clear : function()
6023     {
6024         this.el.select('tbody', true).first().dom.innerHTML = '';
6025     },
6026     /**
6027      * Show or hide a row.
6028      * @param {Number} rowIndex to show or hide
6029      * @param {Boolean} state hide
6030      */
6031     setRowVisibility : function(rowIndex, state)
6032     {
6033         var bt = this.mainBody.dom;
6034         
6035         var rows = this.el.select('tbody > tr', true).elements;
6036         
6037         if(typeof(rows[rowIndex]) == 'undefined'){
6038             return;
6039         }
6040         rows[rowIndex].dom.style.display = state ? '' : 'none';
6041     },
6042     
6043     
6044     getSelectionModel : function(){
6045         if(!this.selModel){
6046             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6047         }
6048         return this.selModel;
6049     },
6050     /*
6051      * Render the Roo.bootstrap object from renderder
6052      */
6053     renderCellObject : function(r)
6054     {
6055         var _this = this;
6056         
6057         var t = r.cfg.render(r.container);
6058         
6059         if(r.cfg.cn){
6060             Roo.each(r.cfg.cn, function(c){
6061                 var child = {
6062                     container: t.getChildContainer(),
6063                     cfg: c
6064                 }
6065                 _this.renderCellObject(child);
6066             })
6067         }
6068     },
6069     
6070     getRowIndex : function(row)
6071     {
6072         var rowIndex = -1;
6073         
6074         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6075             if(el != row){
6076                 return;
6077             }
6078             
6079             rowIndex = index;
6080         });
6081         
6082         return rowIndex;
6083     }
6084    
6085 });
6086
6087  
6088
6089  /*
6090  * - LGPL
6091  *
6092  * table cell
6093  * 
6094  */
6095
6096 /**
6097  * @class Roo.bootstrap.TableCell
6098  * @extends Roo.bootstrap.Component
6099  * Bootstrap TableCell class
6100  * @cfg {String} html cell contain text
6101  * @cfg {String} cls cell class
6102  * @cfg {String} tag cell tag (td|th) default td
6103  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6104  * @cfg {String} align Aligns the content in a cell
6105  * @cfg {String} axis Categorizes cells
6106  * @cfg {String} bgcolor Specifies the background color of a cell
6107  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6108  * @cfg {Number} colspan Specifies the number of columns a cell should span
6109  * @cfg {String} headers Specifies one or more header cells a cell is related to
6110  * @cfg {Number} height Sets the height of a cell
6111  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6112  * @cfg {Number} rowspan Sets the number of rows a cell should span
6113  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6114  * @cfg {String} valign Vertical aligns the content in a cell
6115  * @cfg {Number} width Specifies the width of a cell
6116  * 
6117  * @constructor
6118  * Create a new TableCell
6119  * @param {Object} config The config object
6120  */
6121
6122 Roo.bootstrap.TableCell = function(config){
6123     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6124 };
6125
6126 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6127     
6128     html: false,
6129     cls: false,
6130     tag: false,
6131     abbr: false,
6132     align: false,
6133     axis: false,
6134     bgcolor: false,
6135     charoff: false,
6136     colspan: false,
6137     headers: false,
6138     height: false,
6139     nowrap: false,
6140     rowspan: false,
6141     scope: false,
6142     valign: false,
6143     width: false,
6144     
6145     
6146     getAutoCreate : function(){
6147         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6148         
6149         cfg = {
6150             tag: 'td'
6151         }
6152         
6153         if(this.tag){
6154             cfg.tag = this.tag;
6155         }
6156         
6157         if (this.html) {
6158             cfg.html=this.html
6159         }
6160         if (this.cls) {
6161             cfg.cls=this.cls
6162         }
6163         if (this.abbr) {
6164             cfg.abbr=this.abbr
6165         }
6166         if (this.align) {
6167             cfg.align=this.align
6168         }
6169         if (this.axis) {
6170             cfg.axis=this.axis
6171         }
6172         if (this.bgcolor) {
6173             cfg.bgcolor=this.bgcolor
6174         }
6175         if (this.charoff) {
6176             cfg.charoff=this.charoff
6177         }
6178         if (this.colspan) {
6179             cfg.colspan=this.colspan
6180         }
6181         if (this.headers) {
6182             cfg.headers=this.headers
6183         }
6184         if (this.height) {
6185             cfg.height=this.height
6186         }
6187         if (this.nowrap) {
6188             cfg.nowrap=this.nowrap
6189         }
6190         if (this.rowspan) {
6191             cfg.rowspan=this.rowspan
6192         }
6193         if (this.scope) {
6194             cfg.scope=this.scope
6195         }
6196         if (this.valign) {
6197             cfg.valign=this.valign
6198         }
6199         if (this.width) {
6200             cfg.width=this.width
6201         }
6202         
6203         
6204         return cfg;
6205     }
6206    
6207 });
6208
6209  
6210
6211  /*
6212  * - LGPL
6213  *
6214  * table row
6215  * 
6216  */
6217
6218 /**
6219  * @class Roo.bootstrap.TableRow
6220  * @extends Roo.bootstrap.Component
6221  * Bootstrap TableRow class
6222  * @cfg {String} cls row class
6223  * @cfg {String} align Aligns the content in a table row
6224  * @cfg {String} bgcolor Specifies a background color for a table row
6225  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6226  * @cfg {String} valign Vertical aligns the content in a table row
6227  * 
6228  * @constructor
6229  * Create a new TableRow
6230  * @param {Object} config The config object
6231  */
6232
6233 Roo.bootstrap.TableRow = function(config){
6234     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6235 };
6236
6237 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6238     
6239     cls: false,
6240     align: false,
6241     bgcolor: false,
6242     charoff: false,
6243     valign: false,
6244     
6245     getAutoCreate : function(){
6246         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6247         
6248         cfg = {
6249             tag: 'tr'
6250         }
6251             
6252         if(this.cls){
6253             cfg.cls = this.cls;
6254         }
6255         if(this.align){
6256             cfg.align = this.align;
6257         }
6258         if(this.bgcolor){
6259             cfg.bgcolor = this.bgcolor;
6260         }
6261         if(this.charoff){
6262             cfg.charoff = this.charoff;
6263         }
6264         if(this.valign){
6265             cfg.valign = this.valign;
6266         }
6267         
6268         return cfg;
6269     }
6270    
6271 });
6272
6273  
6274
6275  /*
6276  * - LGPL
6277  *
6278  * table body
6279  * 
6280  */
6281
6282 /**
6283  * @class Roo.bootstrap.TableBody
6284  * @extends Roo.bootstrap.Component
6285  * Bootstrap TableBody class
6286  * @cfg {String} cls element class
6287  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6288  * @cfg {String} align Aligns the content inside the element
6289  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6290  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6291  * 
6292  * @constructor
6293  * Create a new TableBody
6294  * @param {Object} config The config object
6295  */
6296
6297 Roo.bootstrap.TableBody = function(config){
6298     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6299 };
6300
6301 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6302     
6303     cls: false,
6304     tag: false,
6305     align: false,
6306     charoff: false,
6307     valign: false,
6308     
6309     getAutoCreate : function(){
6310         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6311         
6312         cfg = {
6313             tag: 'tbody'
6314         }
6315             
6316         if (this.cls) {
6317             cfg.cls=this.cls
6318         }
6319         if(this.tag){
6320             cfg.tag = this.tag;
6321         }
6322         
6323         if(this.align){
6324             cfg.align = this.align;
6325         }
6326         if(this.charoff){
6327             cfg.charoff = this.charoff;
6328         }
6329         if(this.valign){
6330             cfg.valign = this.valign;
6331         }
6332         
6333         return cfg;
6334     }
6335     
6336     
6337 //    initEvents : function()
6338 //    {
6339 //        
6340 //        if(!this.store){
6341 //            return;
6342 //        }
6343 //        
6344 //        this.store = Roo.factory(this.store, Roo.data);
6345 //        this.store.on('load', this.onLoad, this);
6346 //        
6347 //        this.store.load();
6348 //        
6349 //    },
6350 //    
6351 //    onLoad: function () 
6352 //    {   
6353 //        this.fireEvent('load', this);
6354 //    }
6355 //    
6356 //   
6357 });
6358
6359  
6360
6361  /*
6362  * Based on:
6363  * Ext JS Library 1.1.1
6364  * Copyright(c) 2006-2007, Ext JS, LLC.
6365  *
6366  * Originally Released Under LGPL - original licence link has changed is not relivant.
6367  *
6368  * Fork - LGPL
6369  * <script type="text/javascript">
6370  */
6371
6372 // as we use this in bootstrap.
6373 Roo.namespace('Roo.form');
6374  /**
6375  * @class Roo.form.Action
6376  * Internal Class used to handle form actions
6377  * @constructor
6378  * @param {Roo.form.BasicForm} el The form element or its id
6379  * @param {Object} config Configuration options
6380  */
6381
6382  
6383  
6384 // define the action interface
6385 Roo.form.Action = function(form, options){
6386     this.form = form;
6387     this.options = options || {};
6388 };
6389 /**
6390  * Client Validation Failed
6391  * @const 
6392  */
6393 Roo.form.Action.CLIENT_INVALID = 'client';
6394 /**
6395  * Server Validation Failed
6396  * @const 
6397  */
6398 Roo.form.Action.SERVER_INVALID = 'server';
6399  /**
6400  * Connect to Server Failed
6401  * @const 
6402  */
6403 Roo.form.Action.CONNECT_FAILURE = 'connect';
6404 /**
6405  * Reading Data from Server Failed
6406  * @const 
6407  */
6408 Roo.form.Action.LOAD_FAILURE = 'load';
6409
6410 Roo.form.Action.prototype = {
6411     type : 'default',
6412     failureType : undefined,
6413     response : undefined,
6414     result : undefined,
6415
6416     // interface method
6417     run : function(options){
6418
6419     },
6420
6421     // interface method
6422     success : function(response){
6423
6424     },
6425
6426     // interface method
6427     handleResponse : function(response){
6428
6429     },
6430
6431     // default connection failure
6432     failure : function(response){
6433         
6434         this.response = response;
6435         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6436         this.form.afterAction(this, false);
6437     },
6438
6439     processResponse : function(response){
6440         this.response = response;
6441         if(!response.responseText){
6442             return true;
6443         }
6444         this.result = this.handleResponse(response);
6445         return this.result;
6446     },
6447
6448     // utility functions used internally
6449     getUrl : function(appendParams){
6450         var url = this.options.url || this.form.url || this.form.el.dom.action;
6451         if(appendParams){
6452             var p = this.getParams();
6453             if(p){
6454                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6455             }
6456         }
6457         return url;
6458     },
6459
6460     getMethod : function(){
6461         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6462     },
6463
6464     getParams : function(){
6465         var bp = this.form.baseParams;
6466         var p = this.options.params;
6467         if(p){
6468             if(typeof p == "object"){
6469                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6470             }else if(typeof p == 'string' && bp){
6471                 p += '&' + Roo.urlEncode(bp);
6472             }
6473         }else if(bp){
6474             p = Roo.urlEncode(bp);
6475         }
6476         return p;
6477     },
6478
6479     createCallback : function(){
6480         return {
6481             success: this.success,
6482             failure: this.failure,
6483             scope: this,
6484             timeout: (this.form.timeout*1000),
6485             upload: this.form.fileUpload ? this.success : undefined
6486         };
6487     }
6488 };
6489
6490 Roo.form.Action.Submit = function(form, options){
6491     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6492 };
6493
6494 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6495     type : 'submit',
6496
6497     haveProgress : false,
6498     uploadComplete : false,
6499     
6500     // uploadProgress indicator.
6501     uploadProgress : function()
6502     {
6503         if (!this.form.progressUrl) {
6504             return;
6505         }
6506         
6507         if (!this.haveProgress) {
6508             Roo.MessageBox.progress("Uploading", "Uploading");
6509         }
6510         if (this.uploadComplete) {
6511            Roo.MessageBox.hide();
6512            return;
6513         }
6514         
6515         this.haveProgress = true;
6516    
6517         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6518         
6519         var c = new Roo.data.Connection();
6520         c.request({
6521             url : this.form.progressUrl,
6522             params: {
6523                 id : uid
6524             },
6525             method: 'GET',
6526             success : function(req){
6527                //console.log(data);
6528                 var rdata = false;
6529                 var edata;
6530                 try  {
6531                    rdata = Roo.decode(req.responseText)
6532                 } catch (e) {
6533                     Roo.log("Invalid data from server..");
6534                     Roo.log(edata);
6535                     return;
6536                 }
6537                 if (!rdata || !rdata.success) {
6538                     Roo.log(rdata);
6539                     Roo.MessageBox.alert(Roo.encode(rdata));
6540                     return;
6541                 }
6542                 var data = rdata.data;
6543                 
6544                 if (this.uploadComplete) {
6545                    Roo.MessageBox.hide();
6546                    return;
6547                 }
6548                    
6549                 if (data){
6550                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6551                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6552                     );
6553                 }
6554                 this.uploadProgress.defer(2000,this);
6555             },
6556        
6557             failure: function(data) {
6558                 Roo.log('progress url failed ');
6559                 Roo.log(data);
6560             },
6561             scope : this
6562         });
6563            
6564     },
6565     
6566     
6567     run : function()
6568     {
6569         // run get Values on the form, so it syncs any secondary forms.
6570         this.form.getValues();
6571         
6572         var o = this.options;
6573         var method = this.getMethod();
6574         var isPost = method == 'POST';
6575         if(o.clientValidation === false || this.form.isValid()){
6576             
6577             if (this.form.progressUrl) {
6578                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6579                     (new Date() * 1) + '' + Math.random());
6580                     
6581             } 
6582             
6583             
6584             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6585                 form:this.form.el.dom,
6586                 url:this.getUrl(!isPost),
6587                 method: method,
6588                 params:isPost ? this.getParams() : null,
6589                 isUpload: this.form.fileUpload
6590             }));
6591             
6592             this.uploadProgress();
6593
6594         }else if (o.clientValidation !== false){ // client validation failed
6595             this.failureType = Roo.form.Action.CLIENT_INVALID;
6596             this.form.afterAction(this, false);
6597         }
6598     },
6599
6600     success : function(response)
6601     {
6602         this.uploadComplete= true;
6603         if (this.haveProgress) {
6604             Roo.MessageBox.hide();
6605         }
6606         
6607         
6608         var result = this.processResponse(response);
6609         if(result === true || result.success){
6610             this.form.afterAction(this, true);
6611             return;
6612         }
6613         if(result.errors){
6614             this.form.markInvalid(result.errors);
6615             this.failureType = Roo.form.Action.SERVER_INVALID;
6616         }
6617         this.form.afterAction(this, false);
6618     },
6619     failure : function(response)
6620     {
6621         this.uploadComplete= true;
6622         if (this.haveProgress) {
6623             Roo.MessageBox.hide();
6624         }
6625         
6626         this.response = response;
6627         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6628         this.form.afterAction(this, false);
6629     },
6630     
6631     handleResponse : function(response){
6632         if(this.form.errorReader){
6633             var rs = this.form.errorReader.read(response);
6634             var errors = [];
6635             if(rs.records){
6636                 for(var i = 0, len = rs.records.length; i < len; i++) {
6637                     var r = rs.records[i];
6638                     errors[i] = r.data;
6639                 }
6640             }
6641             if(errors.length < 1){
6642                 errors = null;
6643             }
6644             return {
6645                 success : rs.success,
6646                 errors : errors
6647             };
6648         }
6649         var ret = false;
6650         try {
6651             ret = Roo.decode(response.responseText);
6652         } catch (e) {
6653             ret = {
6654                 success: false,
6655                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6656                 errors : []
6657             };
6658         }
6659         return ret;
6660         
6661     }
6662 });
6663
6664
6665 Roo.form.Action.Load = function(form, options){
6666     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6667     this.reader = this.form.reader;
6668 };
6669
6670 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6671     type : 'load',
6672
6673     run : function(){
6674         
6675         Roo.Ajax.request(Roo.apply(
6676                 this.createCallback(), {
6677                     method:this.getMethod(),
6678                     url:this.getUrl(false),
6679                     params:this.getParams()
6680         }));
6681     },
6682
6683     success : function(response){
6684         
6685         var result = this.processResponse(response);
6686         if(result === true || !result.success || !result.data){
6687             this.failureType = Roo.form.Action.LOAD_FAILURE;
6688             this.form.afterAction(this, false);
6689             return;
6690         }
6691         this.form.clearInvalid();
6692         this.form.setValues(result.data);
6693         this.form.afterAction(this, true);
6694     },
6695
6696     handleResponse : function(response){
6697         if(this.form.reader){
6698             var rs = this.form.reader.read(response);
6699             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6700             return {
6701                 success : rs.success,
6702                 data : data
6703             };
6704         }
6705         return Roo.decode(response.responseText);
6706     }
6707 });
6708
6709 Roo.form.Action.ACTION_TYPES = {
6710     'load' : Roo.form.Action.Load,
6711     'submit' : Roo.form.Action.Submit
6712 };/*
6713  * - LGPL
6714  *
6715  * form
6716  * 
6717  */
6718
6719 /**
6720  * @class Roo.bootstrap.Form
6721  * @extends Roo.bootstrap.Component
6722  * Bootstrap Form class
6723  * @cfg {String} method  GET | POST (default POST)
6724  * @cfg {String} labelAlign top | left (default top)
6725  * @cfg {String} align left  | right - for navbars
6726  * @cfg {Boolean} loadMask load mask when submit (default true)
6727
6728  * 
6729  * @constructor
6730  * Create a new Form
6731  * @param {Object} config The config object
6732  */
6733
6734
6735 Roo.bootstrap.Form = function(config){
6736     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6737     this.addEvents({
6738         /**
6739          * @event clientvalidation
6740          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6741          * @param {Form} this
6742          * @param {Boolean} valid true if the form has passed client-side validation
6743          */
6744         clientvalidation: true,
6745         /**
6746          * @event beforeaction
6747          * Fires before any action is performed. Return false to cancel the action.
6748          * @param {Form} this
6749          * @param {Action} action The action to be performed
6750          */
6751         beforeaction: true,
6752         /**
6753          * @event actionfailed
6754          * Fires when an action fails.
6755          * @param {Form} this
6756          * @param {Action} action The action that failed
6757          */
6758         actionfailed : true,
6759         /**
6760          * @event actioncomplete
6761          * Fires when an action is completed.
6762          * @param {Form} this
6763          * @param {Action} action The action that completed
6764          */
6765         actioncomplete : true
6766     });
6767     
6768 };
6769
6770 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6771       
6772      /**
6773      * @cfg {String} method
6774      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6775      */
6776     method : 'POST',
6777     /**
6778      * @cfg {String} url
6779      * The URL to use for form actions if one isn't supplied in the action options.
6780      */
6781     /**
6782      * @cfg {Boolean} fileUpload
6783      * Set to true if this form is a file upload.
6784      */
6785      
6786     /**
6787      * @cfg {Object} baseParams
6788      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6789      */
6790       
6791     /**
6792      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6793      */
6794     timeout: 30,
6795     /**
6796      * @cfg {Sting} align (left|right) for navbar forms
6797      */
6798     align : 'left',
6799
6800     // private
6801     activeAction : null,
6802  
6803     /**
6804      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6805      * element by passing it or its id or mask the form itself by passing in true.
6806      * @type Mixed
6807      */
6808     waitMsgTarget : false,
6809     
6810     loadMask : true,
6811     
6812     getAutoCreate : function(){
6813         
6814         var cfg = {
6815             tag: 'form',
6816             method : this.method || 'POST',
6817             id : this.id || Roo.id(),
6818             cls : ''
6819         }
6820         if (this.parent().xtype.match(/^Nav/)) {
6821             cfg.cls = 'navbar-form navbar-' + this.align;
6822             
6823         }
6824         
6825         if (this.labelAlign == 'left' ) {
6826             cfg.cls += ' form-horizontal';
6827         }
6828         
6829         
6830         return cfg;
6831     },
6832     initEvents : function()
6833     {
6834         this.el.on('submit', this.onSubmit, this);
6835         // this was added as random key presses on the form where triggering form submit.
6836         this.el.on('keypress', function(e) {
6837             if (e.getCharCode() != 13) {
6838                 return true;
6839             }
6840             // we might need to allow it for textareas.. and some other items.
6841             // check e.getTarget().
6842             
6843             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6844                 return true;
6845             }
6846         
6847             Roo.log("keypress blocked");
6848             
6849             e.preventDefault();
6850             return false;
6851         });
6852         
6853     },
6854     // private
6855     onSubmit : function(e){
6856         e.stopEvent();
6857     },
6858     
6859      /**
6860      * Returns true if client-side validation on the form is successful.
6861      * @return Boolean
6862      */
6863     isValid : function(){
6864         var items = this.getItems();
6865         var valid = true;
6866         items.each(function(f){
6867            if(!f.validate()){
6868                valid = false;
6869                
6870            }
6871         });
6872         return valid;
6873     },
6874     /**
6875      * Returns true if any fields in this form have changed since their original load.
6876      * @return Boolean
6877      */
6878     isDirty : function(){
6879         var dirty = false;
6880         var items = this.getItems();
6881         items.each(function(f){
6882            if(f.isDirty()){
6883                dirty = true;
6884                return false;
6885            }
6886            return true;
6887         });
6888         return dirty;
6889     },
6890      /**
6891      * Performs a predefined action (submit or load) or custom actions you define on this form.
6892      * @param {String} actionName The name of the action type
6893      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6894      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6895      * accept other config options):
6896      * <pre>
6897 Property          Type             Description
6898 ----------------  ---------------  ----------------------------------------------------------------------------------
6899 url               String           The url for the action (defaults to the form's url)
6900 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6901 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6902 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6903                                    validate the form on the client (defaults to false)
6904      * </pre>
6905      * @return {BasicForm} this
6906      */
6907     doAction : function(action, options){
6908         if(typeof action == 'string'){
6909             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6910         }
6911         if(this.fireEvent('beforeaction', this, action) !== false){
6912             this.beforeAction(action);
6913             action.run.defer(100, action);
6914         }
6915         return this;
6916     },
6917     
6918     // private
6919     beforeAction : function(action){
6920         var o = action.options;
6921         
6922         if(this.loadMask){
6923             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6924         }
6925         // not really supported yet.. ??
6926         
6927         //if(this.waitMsgTarget === true){
6928         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6929         //}else if(this.waitMsgTarget){
6930         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6931         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6932         //}else {
6933         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6934        // }
6935          
6936     },
6937
6938     // private
6939     afterAction : function(action, success){
6940         this.activeAction = null;
6941         var o = action.options;
6942         
6943         //if(this.waitMsgTarget === true){
6944             this.el.unmask();
6945         //}else if(this.waitMsgTarget){
6946         //    this.waitMsgTarget.unmask();
6947         //}else{
6948         //    Roo.MessageBox.updateProgress(1);
6949         //    Roo.MessageBox.hide();
6950        // }
6951         // 
6952         if(success){
6953             if(o.reset){
6954                 this.reset();
6955             }
6956             Roo.callback(o.success, o.scope, [this, action]);
6957             this.fireEvent('actioncomplete', this, action);
6958             
6959         }else{
6960             
6961             // failure condition..
6962             // we have a scenario where updates need confirming.
6963             // eg. if a locking scenario exists..
6964             // we look for { errors : { needs_confirm : true }} in the response.
6965             if (
6966                 (typeof(action.result) != 'undefined')  &&
6967                 (typeof(action.result.errors) != 'undefined')  &&
6968                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6969            ){
6970                 var _t = this;
6971                 Roo.log("not supported yet");
6972                  /*
6973                 
6974                 Roo.MessageBox.confirm(
6975                     "Change requires confirmation",
6976                     action.result.errorMsg,
6977                     function(r) {
6978                         if (r != 'yes') {
6979                             return;
6980                         }
6981                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6982                     }
6983                     
6984                 );
6985                 */
6986                 
6987                 
6988                 return;
6989             }
6990             
6991             Roo.callback(o.failure, o.scope, [this, action]);
6992             // show an error message if no failed handler is set..
6993             if (!this.hasListener('actionfailed')) {
6994                 Roo.log("need to add dialog support");
6995                 /*
6996                 Roo.MessageBox.alert("Error",
6997                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6998                         action.result.errorMsg :
6999                         "Saving Failed, please check your entries or try again"
7000                 );
7001                 */
7002             }
7003             
7004             this.fireEvent('actionfailed', this, action);
7005         }
7006         
7007     },
7008     /**
7009      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7010      * @param {String} id The value to search for
7011      * @return Field
7012      */
7013     findField : function(id){
7014         var items = this.getItems();
7015         var field = items.get(id);
7016         if(!field){
7017              items.each(function(f){
7018                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7019                     field = f;
7020                     return false;
7021                 }
7022                 return true;
7023             });
7024         }
7025         return field || null;
7026     },
7027      /**
7028      * Mark fields in this form invalid in bulk.
7029      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7030      * @return {BasicForm} this
7031      */
7032     markInvalid : function(errors){
7033         if(errors instanceof Array){
7034             for(var i = 0, len = errors.length; i < len; i++){
7035                 var fieldError = errors[i];
7036                 var f = this.findField(fieldError.id);
7037                 if(f){
7038                     f.markInvalid(fieldError.msg);
7039                 }
7040             }
7041         }else{
7042             var field, id;
7043             for(id in errors){
7044                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7045                     field.markInvalid(errors[id]);
7046                 }
7047             }
7048         }
7049         //Roo.each(this.childForms || [], function (f) {
7050         //    f.markInvalid(errors);
7051         //});
7052         
7053         return this;
7054     },
7055
7056     /**
7057      * Set values for fields in this form in bulk.
7058      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7059      * @return {BasicForm} this
7060      */
7061     setValues : function(values){
7062         if(values instanceof Array){ // array of objects
7063             for(var i = 0, len = values.length; i < len; i++){
7064                 var v = values[i];
7065                 var f = this.findField(v.id);
7066                 if(f){
7067                     f.setValue(v.value);
7068                     if(this.trackResetOnLoad){
7069                         f.originalValue = f.getValue();
7070                     }
7071                 }
7072             }
7073         }else{ // object hash
7074             var field, id;
7075             for(id in values){
7076                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7077                     
7078                     if (field.setFromData && 
7079                         field.valueField && 
7080                         field.displayField &&
7081                         // combos' with local stores can 
7082                         // be queried via setValue()
7083                         // to set their value..
7084                         (field.store && !field.store.isLocal)
7085                         ) {
7086                         // it's a combo
7087                         var sd = { };
7088                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7089                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7090                         field.setFromData(sd);
7091                         
7092                     } else {
7093                         field.setValue(values[id]);
7094                     }
7095                     
7096                     
7097                     if(this.trackResetOnLoad){
7098                         field.originalValue = field.getValue();
7099                     }
7100                 }
7101             }
7102         }
7103          
7104         //Roo.each(this.childForms || [], function (f) {
7105         //    f.setValues(values);
7106         //});
7107                 
7108         return this;
7109     },
7110
7111     /**
7112      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7113      * they are returned as an array.
7114      * @param {Boolean} asString
7115      * @return {Object}
7116      */
7117     getValues : function(asString){
7118         //if (this.childForms) {
7119             // copy values from the child forms
7120         //    Roo.each(this.childForms, function (f) {
7121         //        this.setValues(f.getValues());
7122         //    }, this);
7123         //}
7124         
7125         
7126         
7127         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7128         if(asString === true){
7129             return fs;
7130         }
7131         return Roo.urlDecode(fs);
7132     },
7133     
7134     /**
7135      * Returns the fields in this form as an object with key/value pairs. 
7136      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7137      * @return {Object}
7138      */
7139     getFieldValues : function(with_hidden)
7140     {
7141         var items = this.getItems();
7142         var ret = {};
7143         items.each(function(f){
7144             if (!f.getName()) {
7145                 return;
7146             }
7147             var v = f.getValue();
7148             if (f.inputType =='radio') {
7149                 if (typeof(ret[f.getName()]) == 'undefined') {
7150                     ret[f.getName()] = ''; // empty..
7151                 }
7152                 
7153                 if (!f.el.dom.checked) {
7154                     return;
7155                     
7156                 }
7157                 v = f.el.dom.value;
7158                 
7159             }
7160             
7161             // not sure if this supported any more..
7162             if ((typeof(v) == 'object') && f.getRawValue) {
7163                 v = f.getRawValue() ; // dates..
7164             }
7165             // combo boxes where name != hiddenName...
7166             if (f.name != f.getName()) {
7167                 ret[f.name] = f.getRawValue();
7168             }
7169             ret[f.getName()] = v;
7170         });
7171         
7172         return ret;
7173     },
7174
7175     /**
7176      * Clears all invalid messages in this form.
7177      * @return {BasicForm} this
7178      */
7179     clearInvalid : function(){
7180         var items = this.getItems();
7181         
7182         items.each(function(f){
7183            f.clearInvalid();
7184         });
7185         
7186         
7187         
7188         return this;
7189     },
7190
7191     /**
7192      * Resets this form.
7193      * @return {BasicForm} this
7194      */
7195     reset : function(){
7196         var items = this.getItems();
7197         items.each(function(f){
7198             f.reset();
7199         });
7200         
7201         Roo.each(this.childForms || [], function (f) {
7202             f.reset();
7203         });
7204        
7205         
7206         return this;
7207     },
7208     getItems : function()
7209     {
7210         var r=new Roo.util.MixedCollection(false, function(o){
7211             return o.id || (o.id = Roo.id());
7212         });
7213         var iter = function(el) {
7214             if (el.inputEl) {
7215                 r.add(el);
7216             }
7217             if (!el.items) {
7218                 return;
7219             }
7220             Roo.each(el.items,function(e) {
7221                 iter(e);
7222             });
7223             
7224             
7225         };
7226         
7227         iter(this);
7228         return r;
7229         
7230         
7231         
7232         
7233     }
7234     
7235 });
7236
7237  
7238 /*
7239  * Based on:
7240  * Ext JS Library 1.1.1
7241  * Copyright(c) 2006-2007, Ext JS, LLC.
7242  *
7243  * Originally Released Under LGPL - original licence link has changed is not relivant.
7244  *
7245  * Fork - LGPL
7246  * <script type="text/javascript">
7247  */
7248 /**
7249  * @class Roo.form.VTypes
7250  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7251  * @singleton
7252  */
7253 Roo.form.VTypes = function(){
7254     // closure these in so they are only created once.
7255     var alpha = /^[a-zA-Z_]+$/;
7256     var alphanum = /^[a-zA-Z0-9_]+$/;
7257     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7258     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7259
7260     // All these messages and functions are configurable
7261     return {
7262         /**
7263          * The function used to validate email addresses
7264          * @param {String} value The email address
7265          */
7266         'email' : function(v){
7267             return email.test(v);
7268         },
7269         /**
7270          * The error text to display when the email validation function returns false
7271          * @type String
7272          */
7273         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7274         /**
7275          * The keystroke filter mask to be applied on email input
7276          * @type RegExp
7277          */
7278         'emailMask' : /[a-z0-9_\.\-@]/i,
7279
7280         /**
7281          * The function used to validate URLs
7282          * @param {String} value The URL
7283          */
7284         'url' : function(v){
7285             return url.test(v);
7286         },
7287         /**
7288          * The error text to display when the url validation function returns false
7289          * @type String
7290          */
7291         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7292         
7293         /**
7294          * The function used to validate alpha values
7295          * @param {String} value The value
7296          */
7297         'alpha' : function(v){
7298             return alpha.test(v);
7299         },
7300         /**
7301          * The error text to display when the alpha validation function returns false
7302          * @type String
7303          */
7304         'alphaText' : 'This field should only contain letters and _',
7305         /**
7306          * The keystroke filter mask to be applied on alpha input
7307          * @type RegExp
7308          */
7309         'alphaMask' : /[a-z_]/i,
7310
7311         /**
7312          * The function used to validate alphanumeric values
7313          * @param {String} value The value
7314          */
7315         'alphanum' : function(v){
7316             return alphanum.test(v);
7317         },
7318         /**
7319          * The error text to display when the alphanumeric validation function returns false
7320          * @type String
7321          */
7322         'alphanumText' : 'This field should only contain letters, numbers and _',
7323         /**
7324          * The keystroke filter mask to be applied on alphanumeric input
7325          * @type RegExp
7326          */
7327         'alphanumMask' : /[a-z0-9_]/i
7328     };
7329 }();/*
7330  * - LGPL
7331  *
7332  * Input
7333  * 
7334  */
7335
7336 /**
7337  * @class Roo.bootstrap.Input
7338  * @extends Roo.bootstrap.Component
7339  * Bootstrap Input class
7340  * @cfg {Boolean} disabled is it disabled
7341  * @cfg {String} fieldLabel - the label associated
7342  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7343  * @cfg {String} name name of the input
7344  * @cfg {string} fieldLabel - the label associated
7345  * @cfg {string}  inputType - input / file submit ...
7346  * @cfg {string} placeholder - placeholder to put in text.
7347  * @cfg {string}  before - input group add on before
7348  * @cfg {string} after - input group add on after
7349  * @cfg {string} size - (lg|sm) or leave empty..
7350  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7351  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7352  * @cfg {Number} md colspan out of 12 for computer-sized screens
7353  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7354  * @cfg {string} value default value of the input
7355  * @cfg {Number} labelWidth set the width of label (0-12)
7356  * @cfg {String} labelAlign (top|left)
7357  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7358  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7359
7360  * @cfg {String} align (left|center|right) Default left
7361  * 
7362  * 
7363  * 
7364  * @constructor
7365  * Create a new Input
7366  * @param {Object} config The config object
7367  */
7368
7369 Roo.bootstrap.Input = function(config){
7370     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7371    
7372         this.addEvents({
7373             /**
7374              * @event focus
7375              * Fires when this field receives input focus.
7376              * @param {Roo.form.Field} this
7377              */
7378             focus : true,
7379             /**
7380              * @event blur
7381              * Fires when this field loses input focus.
7382              * @param {Roo.form.Field} this
7383              */
7384             blur : true,
7385             /**
7386              * @event specialkey
7387              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7388              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7389              * @param {Roo.form.Field} this
7390              * @param {Roo.EventObject} e The event object
7391              */
7392             specialkey : true,
7393             /**
7394              * @event change
7395              * Fires just before the field blurs if the field value has changed.
7396              * @param {Roo.form.Field} this
7397              * @param {Mixed} newValue The new value
7398              * @param {Mixed} oldValue The original value
7399              */
7400             change : true,
7401             /**
7402              * @event invalid
7403              * Fires after the field has been marked as invalid.
7404              * @param {Roo.form.Field} this
7405              * @param {String} msg The validation message
7406              */
7407             invalid : true,
7408             /**
7409              * @event valid
7410              * Fires after the field has been validated with no errors.
7411              * @param {Roo.form.Field} this
7412              */
7413             valid : true,
7414              /**
7415              * @event keyup
7416              * Fires after the key up
7417              * @param {Roo.form.Field} this
7418              * @param {Roo.EventObject}  e The event Object
7419              */
7420             keyup : true
7421         });
7422 };
7423
7424 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7425      /**
7426      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7427       automatic validation (defaults to "keyup").
7428      */
7429     validationEvent : "keyup",
7430      /**
7431      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7432      */
7433     validateOnBlur : true,
7434     /**
7435      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7436      */
7437     validationDelay : 250,
7438      /**
7439      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7440      */
7441     focusClass : "x-form-focus",  // not needed???
7442     
7443        
7444     /**
7445      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7446      */
7447     invalidClass : "has-warning",
7448     
7449     /**
7450      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7451      */
7452     validClass : "has-success",
7453     
7454     /**
7455      * @cfg {Boolean} hasFeedback (true|false) default true
7456      */
7457     hasFeedback : true,
7458     
7459     /**
7460      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7461      */
7462     invalidFeedbackClass : "glyphicon-warning-sign",
7463     
7464     /**
7465      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7466      */
7467     validFeedbackClass : "glyphicon-ok",
7468     
7469     /**
7470      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7471      */
7472     selectOnFocus : false,
7473     
7474      /**
7475      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7476      */
7477     maskRe : null,
7478        /**
7479      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7480      */
7481     vtype : null,
7482     
7483       /**
7484      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7485      */
7486     disableKeyFilter : false,
7487     
7488        /**
7489      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7490      */
7491     disabled : false,
7492      /**
7493      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7494      */
7495     allowBlank : true,
7496     /**
7497      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7498      */
7499     blankText : "This field is required",
7500     
7501      /**
7502      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7503      */
7504     minLength : 0,
7505     /**
7506      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7507      */
7508     maxLength : Number.MAX_VALUE,
7509     /**
7510      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7511      */
7512     minLengthText : "The minimum length for this field is {0}",
7513     /**
7514      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7515      */
7516     maxLengthText : "The maximum length for this field is {0}",
7517   
7518     
7519     /**
7520      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7521      * If available, this function will be called only after the basic validators all return true, and will be passed the
7522      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7523      */
7524     validator : null,
7525     /**
7526      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7527      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7528      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7529      */
7530     regex : null,
7531     /**
7532      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7533      */
7534     regexText : "",
7535     
7536     autocomplete: false,
7537     
7538     
7539     fieldLabel : '',
7540     inputType : 'text',
7541     
7542     name : false,
7543     placeholder: false,
7544     before : false,
7545     after : false,
7546     size : false,
7547     hasFocus : false,
7548     preventMark: false,
7549     isFormField : true,
7550     value : '',
7551     labelWidth : 2,
7552     labelAlign : false,
7553     readOnly : false,
7554     align : false,
7555     formatedValue : false,
7556     
7557     parentLabelAlign : function()
7558     {
7559         var parent = this;
7560         while (parent.parent()) {
7561             parent = parent.parent();
7562             if (typeof(parent.labelAlign) !='undefined') {
7563                 return parent.labelAlign;
7564             }
7565         }
7566         return 'left';
7567         
7568     },
7569     
7570     getAutoCreate : function(){
7571         
7572         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7573         
7574         var id = Roo.id();
7575         
7576         var cfg = {};
7577         
7578         if(this.inputType != 'hidden'){
7579             cfg.cls = 'form-group' //input-group
7580         }
7581         
7582         var input =  {
7583             tag: 'input',
7584             id : id,
7585             type : this.inputType,
7586             value : this.value,
7587             cls : 'form-control',
7588             placeholder : this.placeholder || '',
7589             autocomplete : this.autocomplete || 'new-password'
7590         };
7591         
7592         
7593         if(this.align){
7594             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7595         }
7596         
7597         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7598             input.maxLength = this.maxLength;
7599         }
7600         
7601         if (this.disabled) {
7602             input.disabled=true;
7603         }
7604         
7605         if (this.readOnly) {
7606             input.readonly=true;
7607         }
7608         
7609         if (this.name) {
7610             input.name = this.name;
7611         }
7612         if (this.size) {
7613             input.cls += ' input-' + this.size;
7614         }
7615         var settings=this;
7616         ['xs','sm','md','lg'].map(function(size){
7617             if (settings[size]) {
7618                 cfg.cls += ' col-' + size + '-' + settings[size];
7619             }
7620         });
7621         
7622         var inputblock = input;
7623         
7624         var feedback = {
7625             tag: 'span',
7626             cls: 'glyphicon form-control-feedback'
7627         };
7628             
7629         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7630             
7631             inputblock = {
7632                 cls : 'has-feedback',
7633                 cn :  [
7634                     input,
7635                     feedback
7636                 ] 
7637             };  
7638         }
7639         
7640         if (this.before || this.after) {
7641             
7642             inputblock = {
7643                 cls : 'input-group',
7644                 cn :  [] 
7645             };
7646             
7647             if (this.before && typeof(this.before) == 'string') {
7648                 
7649                 inputblock.cn.push({
7650                     tag :'span',
7651                     cls : 'roo-input-before input-group-addon',
7652                     html : this.before
7653                 });
7654             }
7655             if (this.before && typeof(this.before) == 'object') {
7656                 this.before = Roo.factory(this.before);
7657                 Roo.log(this.before);
7658                 inputblock.cn.push({
7659                     tag :'span',
7660                     cls : 'roo-input-before input-group-' +
7661                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7662                 });
7663             }
7664             
7665             inputblock.cn.push(input);
7666             
7667             if (this.after && typeof(this.after) == 'string') {
7668                 inputblock.cn.push({
7669                     tag :'span',
7670                     cls : 'roo-input-after input-group-addon',
7671                     html : this.after
7672                 });
7673             }
7674             if (this.after && typeof(this.after) == 'object') {
7675                 this.after = Roo.factory(this.after);
7676                 Roo.log(this.after);
7677                 inputblock.cn.push({
7678                     tag :'span',
7679                     cls : 'roo-input-after input-group-' +
7680                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7681                 });
7682             }
7683             
7684             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7685                 inputblock.cls += ' has-feedback';
7686                 inputblock.cn.push(feedback);
7687             }
7688         };
7689         
7690         if (align ==='left' && this.fieldLabel.length) {
7691                 Roo.log("left and has label");
7692                 cfg.cn = [
7693                     
7694                     {
7695                         tag: 'label',
7696                         'for' :  id,
7697                         cls : 'control-label col-sm-' + this.labelWidth,
7698                         html : this.fieldLabel
7699                         
7700                     },
7701                     {
7702                         cls : "col-sm-" + (12 - this.labelWidth), 
7703                         cn: [
7704                             inputblock
7705                         ]
7706                     }
7707                     
7708                 ];
7709         } else if ( this.fieldLabel.length) {
7710                 Roo.log(" label");
7711                  cfg.cn = [
7712                    
7713                     {
7714                         tag: 'label',
7715                         //cls : 'input-group-addon',
7716                         html : this.fieldLabel
7717                         
7718                     },
7719                     
7720                     inputblock
7721                     
7722                 ];
7723
7724         } else {
7725             
7726                 Roo.log(" no label && no align");
7727                 cfg.cn = [
7728                     
7729                         inputblock
7730                     
7731                 ];
7732                 
7733                 
7734         };
7735         Roo.log('input-parentType: ' + this.parentType);
7736         
7737         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7738            cfg.cls += ' navbar-form';
7739            Roo.log(cfg);
7740         }
7741         
7742         return cfg;
7743         
7744     },
7745     /**
7746      * return the real input element.
7747      */
7748     inputEl: function ()
7749     {
7750         return this.el.select('input.form-control',true).first();
7751     },
7752     
7753     tooltipEl : function()
7754     {
7755         return this.inputEl();
7756     },
7757     
7758     setDisabled : function(v)
7759     {
7760         var i  = this.inputEl().dom;
7761         if (!v) {
7762             i.removeAttribute('disabled');
7763             return;
7764             
7765         }
7766         i.setAttribute('disabled','true');
7767     },
7768     initEvents : function()
7769     {
7770           
7771         this.inputEl().on("keydown" , this.fireKey,  this);
7772         this.inputEl().on("focus", this.onFocus,  this);
7773         this.inputEl().on("blur", this.onBlur,  this);
7774         
7775         this.inputEl().relayEvent('keyup', this);
7776
7777         // reference to original value for reset
7778         this.originalValue = this.getValue();
7779         //Roo.form.TextField.superclass.initEvents.call(this);
7780         if(this.validationEvent == 'keyup'){
7781             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7782             this.inputEl().on('keyup', this.filterValidation, this);
7783         }
7784         else if(this.validationEvent !== false){
7785             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7786         }
7787         
7788         if(this.selectOnFocus){
7789             this.on("focus", this.preFocus, this);
7790             
7791         }
7792         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7793             this.inputEl().on("keypress", this.filterKeys, this);
7794         }
7795        /* if(this.grow){
7796             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7797             this.el.on("click", this.autoSize,  this);
7798         }
7799         */
7800         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7801             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7802         }
7803         
7804         if (typeof(this.before) == 'object') {
7805             this.before.render(this.el.select('.roo-input-before',true).first());
7806         }
7807         if (typeof(this.after) == 'object') {
7808             this.after.render(this.el.select('.roo-input-after',true).first());
7809         }
7810         
7811         
7812     },
7813     filterValidation : function(e){
7814         if(!e.isNavKeyPress()){
7815             this.validationTask.delay(this.validationDelay);
7816         }
7817     },
7818      /**
7819      * Validates the field value
7820      * @return {Boolean} True if the value is valid, else false
7821      */
7822     validate : function(){
7823         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7824         if(this.disabled || this.validateValue(this.getRawValue())){
7825             this.markValid();
7826             return true;
7827         }
7828         
7829         this.markInvalid();
7830         return false;
7831     },
7832     
7833     
7834     /**
7835      * Validates a value according to the field's validation rules and marks the field as invalid
7836      * if the validation fails
7837      * @param {Mixed} value The value to validate
7838      * @return {Boolean} True if the value is valid, else false
7839      */
7840     validateValue : function(value){
7841         if(value.length < 1)  { // if it's blank
7842             if(this.allowBlank){
7843                 return true;
7844             }
7845             return false;
7846         }
7847         
7848         if(value.length < this.minLength){
7849             return false;
7850         }
7851         if(value.length > this.maxLength){
7852             return false;
7853         }
7854         if(this.vtype){
7855             var vt = Roo.form.VTypes;
7856             if(!vt[this.vtype](value, this)){
7857                 return false;
7858             }
7859         }
7860         if(typeof this.validator == "function"){
7861             var msg = this.validator(value);
7862             if(msg !== true){
7863                 return false;
7864             }
7865         }
7866         
7867         if(this.regex && !this.regex.test(value)){
7868             return false;
7869         }
7870         
7871         return true;
7872     },
7873
7874     
7875     
7876      // private
7877     fireKey : function(e){
7878         //Roo.log('field ' + e.getKey());
7879         if(e.isNavKeyPress()){
7880             this.fireEvent("specialkey", this, e);
7881         }
7882     },
7883     focus : function (selectText){
7884         if(this.rendered){
7885             this.inputEl().focus();
7886             if(selectText === true){
7887                 this.inputEl().dom.select();
7888             }
7889         }
7890         return this;
7891     } ,
7892     
7893     onFocus : function(){
7894         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7895            // this.el.addClass(this.focusClass);
7896         }
7897         if(!this.hasFocus){
7898             this.hasFocus = true;
7899             this.startValue = this.getValue();
7900             this.fireEvent("focus", this);
7901         }
7902     },
7903     
7904     beforeBlur : Roo.emptyFn,
7905
7906     
7907     // private
7908     onBlur : function(){
7909         this.beforeBlur();
7910         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7911             //this.el.removeClass(this.focusClass);
7912         }
7913         this.hasFocus = false;
7914         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7915             this.validate();
7916         }
7917         var v = this.getValue();
7918         if(String(v) !== String(this.startValue)){
7919             this.fireEvent('change', this, v, this.startValue);
7920         }
7921         this.fireEvent("blur", this);
7922     },
7923     
7924     /**
7925      * Resets the current field value to the originally loaded value and clears any validation messages
7926      */
7927     reset : function(){
7928         this.setValue(this.originalValue);
7929         this.validate();
7930     },
7931      /**
7932      * Returns the name of the field
7933      * @return {Mixed} name The name field
7934      */
7935     getName: function(){
7936         return this.name;
7937     },
7938      /**
7939      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7940      * @return {Mixed} value The field value
7941      */
7942     getValue : function(){
7943         
7944         var v = this.inputEl().getValue();
7945         
7946         return v;
7947     },
7948     /**
7949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7950      * @return {Mixed} value The field value
7951      */
7952     getRawValue : function(){
7953         var v = this.inputEl().getValue();
7954         
7955         return v;
7956     },
7957     
7958     /**
7959      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7960      * @param {Mixed} value The value to set
7961      */
7962     setRawValue : function(v){
7963         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7964     },
7965     
7966     selectText : function(start, end){
7967         var v = this.getRawValue();
7968         if(v.length > 0){
7969             start = start === undefined ? 0 : start;
7970             end = end === undefined ? v.length : end;
7971             var d = this.inputEl().dom;
7972             if(d.setSelectionRange){
7973                 d.setSelectionRange(start, end);
7974             }else if(d.createTextRange){
7975                 var range = d.createTextRange();
7976                 range.moveStart("character", start);
7977                 range.moveEnd("character", v.length-end);
7978                 range.select();
7979             }
7980         }
7981     },
7982     
7983     /**
7984      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7985      * @param {Mixed} value The value to set
7986      */
7987     setValue : function(v){
7988         this.value = v;
7989         if(this.rendered){
7990             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7991             this.validate();
7992         }
7993     },
7994     
7995     /*
7996     processValue : function(value){
7997         if(this.stripCharsRe){
7998             var newValue = value.replace(this.stripCharsRe, '');
7999             if(newValue !== value){
8000                 this.setRawValue(newValue);
8001                 return newValue;
8002             }
8003         }
8004         return value;
8005     },
8006   */
8007     preFocus : function(){
8008         
8009         if(this.selectOnFocus){
8010             this.inputEl().dom.select();
8011         }
8012     },
8013     filterKeys : function(e){
8014         var k = e.getKey();
8015         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8016             return;
8017         }
8018         var c = e.getCharCode(), cc = String.fromCharCode(c);
8019         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8020             return;
8021         }
8022         if(!this.maskRe.test(cc)){
8023             e.stopEvent();
8024         }
8025     },
8026      /**
8027      * Clear any invalid styles/messages for this field
8028      */
8029     clearInvalid : function(){
8030         
8031         if(!this.el || this.preventMark){ // not rendered
8032             return;
8033         }
8034         this.el.removeClass(this.invalidClass);
8035         
8036         this.fireEvent('valid', this);
8037     },
8038     
8039      /**
8040      * Mark this field as valid
8041      */
8042     markValid : function(){
8043         if(!this.el  || this.preventMark){ // not rendered
8044             return;
8045         }
8046         
8047         this.el.removeClass([this.invalidClass, this.validClass]);
8048         
8049         if(this.disabled || this.allowBlank){
8050             return;
8051         }
8052         
8053         this.el.addClass(this.validClass);
8054         
8055         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8056             
8057             var feedback = this.el.select('.form-control-feedback', true).first();
8058             
8059             if(feedback){
8060                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8061                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8062             }
8063             
8064         }
8065         
8066         this.fireEvent('valid', this);
8067     },
8068     
8069      /**
8070      * Mark this field as invalid
8071      * @param {String} msg The validation message
8072      */
8073     markInvalid : function(msg){
8074         if(!this.el  || this.preventMark){ // not rendered
8075             return;
8076         }
8077         
8078         this.el.removeClass([this.invalidClass, this.validClass]);
8079         
8080         if(this.disabled || this.allowBlank){
8081             return;
8082         }
8083         
8084         this.el.addClass(this.invalidClass);
8085         
8086         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8087             
8088             var feedback = this.el.select('.form-control-feedback', true).first();
8089             
8090             if(feedback){
8091                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8092                 
8093                 if(this.getValue().length){
8094                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8095                 }
8096                 
8097             }
8098             
8099         }
8100         
8101         this.fireEvent('invalid', this, msg);
8102     },
8103     // private
8104     SafariOnKeyDown : function(event)
8105     {
8106         // this is a workaround for a password hang bug on chrome/ webkit.
8107         
8108         var isSelectAll = false;
8109         
8110         if(this.inputEl().dom.selectionEnd > 0){
8111             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8112         }
8113         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8114             event.preventDefault();
8115             this.setValue('');
8116             return;
8117         }
8118         
8119         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8120             
8121             event.preventDefault();
8122             // this is very hacky as keydown always get's upper case.
8123             //
8124             var cc = String.fromCharCode(event.getCharCode());
8125             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8126             
8127         }
8128     },
8129     adjustWidth : function(tag, w){
8130         tag = tag.toLowerCase();
8131         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8132             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8133                 if(tag == 'input'){
8134                     return w + 2;
8135                 }
8136                 if(tag == 'textarea'){
8137                     return w-2;
8138                 }
8139             }else if(Roo.isOpera){
8140                 if(tag == 'input'){
8141                     return w + 2;
8142                 }
8143                 if(tag == 'textarea'){
8144                     return w-2;
8145                 }
8146             }
8147         }
8148         return w;
8149     }
8150     
8151 });
8152
8153  
8154 /*
8155  * - LGPL
8156  *
8157  * Input
8158  * 
8159  */
8160
8161 /**
8162  * @class Roo.bootstrap.TextArea
8163  * @extends Roo.bootstrap.Input
8164  * Bootstrap TextArea class
8165  * @cfg {Number} cols Specifies the visible width of a text area
8166  * @cfg {Number} rows Specifies the visible number of lines in a text area
8167  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8168  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8169  * @cfg {string} html text
8170  * 
8171  * @constructor
8172  * Create a new TextArea
8173  * @param {Object} config The config object
8174  */
8175
8176 Roo.bootstrap.TextArea = function(config){
8177     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8178    
8179 };
8180
8181 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8182      
8183     cols : false,
8184     rows : 5,
8185     readOnly : false,
8186     warp : 'soft',
8187     resize : false,
8188     value: false,
8189     html: false,
8190     
8191     getAutoCreate : function(){
8192         
8193         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8194         
8195         var id = Roo.id();
8196         
8197         var cfg = {};
8198         
8199         var input =  {
8200             tag: 'textarea',
8201             id : id,
8202             warp : this.warp,
8203             rows : this.rows,
8204             value : this.value || '',
8205             html: this.html || '',
8206             cls : 'form-control',
8207             placeholder : this.placeholder || '' 
8208             
8209         };
8210         
8211         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8212             input.maxLength = this.maxLength;
8213         }
8214         
8215         if(this.resize){
8216             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8217         }
8218         
8219         if(this.cols){
8220             input.cols = this.cols;
8221         }
8222         
8223         if (this.readOnly) {
8224             input.readonly = true;
8225         }
8226         
8227         if (this.name) {
8228             input.name = this.name;
8229         }
8230         
8231         if (this.size) {
8232             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8233         }
8234         
8235         var settings=this;
8236         ['xs','sm','md','lg'].map(function(size){
8237             if (settings[size]) {
8238                 cfg.cls += ' col-' + size + '-' + settings[size];
8239             }
8240         });
8241         
8242         var inputblock = input;
8243         
8244         if(this.hasFeedback && !this.allowBlank){
8245             
8246             var feedback = {
8247                 tag: 'span',
8248                 cls: 'glyphicon form-control-feedback'
8249             };
8250
8251             inputblock = {
8252                 cls : 'has-feedback',
8253                 cn :  [
8254                     input,
8255                     feedback
8256                 ] 
8257             };  
8258         }
8259         
8260         
8261         if (this.before || this.after) {
8262             
8263             inputblock = {
8264                 cls : 'input-group',
8265                 cn :  [] 
8266             };
8267             if (this.before) {
8268                 inputblock.cn.push({
8269                     tag :'span',
8270                     cls : 'input-group-addon',
8271                     html : this.before
8272                 });
8273             }
8274             
8275             inputblock.cn.push(input);
8276             
8277             if(this.hasFeedback && !this.allowBlank){
8278                 inputblock.cls += ' has-feedback';
8279                 inputblock.cn.push(feedback);
8280             }
8281             
8282             if (this.after) {
8283                 inputblock.cn.push({
8284                     tag :'span',
8285                     cls : 'input-group-addon',
8286                     html : this.after
8287                 });
8288             }
8289             
8290         }
8291         
8292         if (align ==='left' && this.fieldLabel.length) {
8293                 Roo.log("left and has label");
8294                 cfg.cn = [
8295                     
8296                     {
8297                         tag: 'label',
8298                         'for' :  id,
8299                         cls : 'control-label col-sm-' + this.labelWidth,
8300                         html : this.fieldLabel
8301                         
8302                     },
8303                     {
8304                         cls : "col-sm-" + (12 - this.labelWidth), 
8305                         cn: [
8306                             inputblock
8307                         ]
8308                     }
8309                     
8310                 ];
8311         } else if ( this.fieldLabel.length) {
8312                 Roo.log(" label");
8313                  cfg.cn = [
8314                    
8315                     {
8316                         tag: 'label',
8317                         //cls : 'input-group-addon',
8318                         html : this.fieldLabel
8319                         
8320                     },
8321                     
8322                     inputblock
8323                     
8324                 ];
8325
8326         } else {
8327             
8328                    Roo.log(" no label && no align");
8329                 cfg.cn = [
8330                     
8331                         inputblock
8332                     
8333                 ];
8334                 
8335                 
8336         }
8337         
8338         if (this.disabled) {
8339             input.disabled=true;
8340         }
8341         
8342         return cfg;
8343         
8344     },
8345     /**
8346      * return the real textarea element.
8347      */
8348     inputEl: function ()
8349     {
8350         return this.el.select('textarea.form-control',true).first();
8351     }
8352 });
8353
8354  
8355 /*
8356  * - LGPL
8357  *
8358  * trigger field - base class for combo..
8359  * 
8360  */
8361  
8362 /**
8363  * @class Roo.bootstrap.TriggerField
8364  * @extends Roo.bootstrap.Input
8365  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8366  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8367  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8368  * for which you can provide a custom implementation.  For example:
8369  * <pre><code>
8370 var trigger = new Roo.bootstrap.TriggerField();
8371 trigger.onTriggerClick = myTriggerFn;
8372 trigger.applyTo('my-field');
8373 </code></pre>
8374  *
8375  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8376  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8377  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8378  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8379  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8380
8381  * @constructor
8382  * Create a new TriggerField.
8383  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8384  * to the base TextField)
8385  */
8386 Roo.bootstrap.TriggerField = function(config){
8387     this.mimicing = false;
8388     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8389 };
8390
8391 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8392     /**
8393      * @cfg {String} triggerClass A CSS class to apply to the trigger
8394      */
8395      /**
8396      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8397      */
8398     hideTrigger:false,
8399
8400     /** @cfg {Boolean} grow @hide */
8401     /** @cfg {Number} growMin @hide */
8402     /** @cfg {Number} growMax @hide */
8403
8404     /**
8405      * @hide 
8406      * @method
8407      */
8408     autoSize: Roo.emptyFn,
8409     // private
8410     monitorTab : true,
8411     // private
8412     deferHeight : true,
8413
8414     
8415     actionMode : 'wrap',
8416     
8417     caret : false,
8418     
8419     
8420     getAutoCreate : function(){
8421        
8422         var align = this.labelAlign || this.parentLabelAlign();
8423         
8424         var id = Roo.id();
8425         
8426         var cfg = {
8427             cls: 'form-group' //input-group
8428         };
8429         
8430         
8431         var input =  {
8432             tag: 'input',
8433             id : id,
8434             type : this.inputType,
8435             cls : 'form-control',
8436             autocomplete: 'new-password',
8437             placeholder : this.placeholder || '' 
8438             
8439         };
8440         if (this.name) {
8441             input.name = this.name;
8442         }
8443         if (this.size) {
8444             input.cls += ' input-' + this.size;
8445         }
8446         
8447         if (this.disabled) {
8448             input.disabled=true;
8449         }
8450         
8451         var inputblock = input;
8452         
8453         if(this.hasFeedback && !this.allowBlank){
8454             
8455             var feedback = {
8456                 tag: 'span',
8457                 cls: 'glyphicon form-control-feedback'
8458             };
8459
8460             inputblock = {
8461                 cls : 'has-feedback',
8462                 cn :  [
8463                     input,
8464                     feedback
8465                 ] 
8466             };  
8467         }
8468         
8469         if (this.before || this.after) {
8470             
8471             inputblock = {
8472                 cls : 'input-group',
8473                 cn :  [] 
8474             };
8475             if (this.before) {
8476                 inputblock.cn.push({
8477                     tag :'span',
8478                     cls : 'input-group-addon',
8479                     html : this.before
8480                 });
8481             }
8482             
8483             inputblock.cn.push(input);
8484             
8485             if(this.hasFeedback && !this.allowBlank){
8486                 inputblock.cls += ' has-feedback';
8487                 inputblock.cn.push(feedback);
8488             }
8489             
8490             if (this.after) {
8491                 inputblock.cn.push({
8492                     tag :'span',
8493                     cls : 'input-group-addon',
8494                     html : this.after
8495                 });
8496             }
8497             
8498         };
8499         
8500         var box = {
8501             tag: 'div',
8502             cn: [
8503                 {
8504                     tag: 'input',
8505                     type : 'hidden',
8506                     cls: 'form-hidden-field'
8507                 },
8508                 inputblock
8509             ]
8510             
8511         };
8512         
8513         if(this.multiple){
8514             Roo.log('multiple');
8515             
8516             box = {
8517                 tag: 'div',
8518                 cn: [
8519                     {
8520                         tag: 'input',
8521                         type : 'hidden',
8522                         cls: 'form-hidden-field'
8523                     },
8524                     {
8525                         tag: 'ul',
8526                         cls: 'select2-choices',
8527                         cn:[
8528                             {
8529                                 tag: 'li',
8530                                 cls: 'select2-search-field',
8531                                 cn: [
8532
8533                                     inputblock
8534                                 ]
8535                             }
8536                         ]
8537                     }
8538                 ]
8539             }
8540         };
8541         
8542         var combobox = {
8543             cls: 'select2-container input-group',
8544             cn: [
8545                 box
8546 //                {
8547 //                    tag: 'ul',
8548 //                    cls: 'typeahead typeahead-long dropdown-menu',
8549 //                    style: 'display:none'
8550 //                }
8551             ]
8552         };
8553         
8554         if(!this.multiple && this.showToggleBtn){
8555             
8556             var caret = {
8557                         tag: 'span',
8558                         cls: 'caret'
8559              };
8560             if (this.caret != false) {
8561                 caret = {
8562                      tag: 'i',
8563                      cls: 'fa fa-' + this.caret
8564                 };
8565                 
8566             }
8567             
8568             combobox.cn.push({
8569                 tag :'span',
8570                 cls : 'input-group-addon btn dropdown-toggle',
8571                 cn : [
8572                     caret,
8573                     {
8574                         tag: 'span',
8575                         cls: 'combobox-clear',
8576                         cn  : [
8577                             {
8578                                 tag : 'i',
8579                                 cls: 'icon-remove'
8580                             }
8581                         ]
8582                     }
8583                 ]
8584
8585             })
8586         }
8587         
8588         if(this.multiple){
8589             combobox.cls += ' select2-container-multi';
8590         }
8591         
8592         if (align ==='left' && this.fieldLabel.length) {
8593             
8594                 Roo.log("left and has label");
8595                 cfg.cn = [
8596                     
8597                     {
8598                         tag: 'label',
8599                         'for' :  id,
8600                         cls : 'control-label col-sm-' + this.labelWidth,
8601                         html : this.fieldLabel
8602                         
8603                     },
8604                     {
8605                         cls : "col-sm-" + (12 - this.labelWidth), 
8606                         cn: [
8607                             combobox
8608                         ]
8609                     }
8610                     
8611                 ];
8612         } else if ( this.fieldLabel.length) {
8613                 Roo.log(" label");
8614                  cfg.cn = [
8615                    
8616                     {
8617                         tag: 'label',
8618                         //cls : 'input-group-addon',
8619                         html : this.fieldLabel
8620                         
8621                     },
8622                     
8623                     combobox
8624                     
8625                 ];
8626
8627         } else {
8628             
8629                 Roo.log(" no label && no align");
8630                 cfg = combobox
8631                      
8632                 
8633         }
8634          
8635         var settings=this;
8636         ['xs','sm','md','lg'].map(function(size){
8637             if (settings[size]) {
8638                 cfg.cls += ' col-' + size + '-' + settings[size];
8639             }
8640         });
8641         
8642         return cfg;
8643         
8644     },
8645     
8646     
8647     
8648     // private
8649     onResize : function(w, h){
8650 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8651 //        if(typeof w == 'number'){
8652 //            var x = w - this.trigger.getWidth();
8653 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8654 //            this.trigger.setStyle('left', x+'px');
8655 //        }
8656     },
8657
8658     // private
8659     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8660
8661     // private
8662     getResizeEl : function(){
8663         return this.inputEl();
8664     },
8665
8666     // private
8667     getPositionEl : function(){
8668         return this.inputEl();
8669     },
8670
8671     // private
8672     alignErrorIcon : function(){
8673         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8674     },
8675
8676     // private
8677     initEvents : function(){
8678         
8679         this.createList();
8680         
8681         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8682         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8683         if(!this.multiple && this.showToggleBtn){
8684             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8685             if(this.hideTrigger){
8686                 this.trigger.setDisplayed(false);
8687             }
8688             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8689         }
8690         
8691         if(this.multiple){
8692             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8693         }
8694         
8695         //this.trigger.addClassOnOver('x-form-trigger-over');
8696         //this.trigger.addClassOnClick('x-form-trigger-click');
8697         
8698         //if(!this.width){
8699         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8700         //}
8701     },
8702     
8703     createList : function()
8704     {
8705         this.list = Roo.get(document.body).createChild({
8706             tag: 'ul',
8707             cls: 'typeahead typeahead-long dropdown-menu',
8708             style: 'display:none'
8709         });
8710         
8711         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8712         
8713     },
8714
8715     // private
8716     initTrigger : function(){
8717        
8718     },
8719
8720     // private
8721     onDestroy : function(){
8722         if(this.trigger){
8723             this.trigger.removeAllListeners();
8724           //  this.trigger.remove();
8725         }
8726         //if(this.wrap){
8727         //    this.wrap.remove();
8728         //}
8729         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8730     },
8731
8732     // private
8733     onFocus : function(){
8734         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8735         /*
8736         if(!this.mimicing){
8737             this.wrap.addClass('x-trigger-wrap-focus');
8738             this.mimicing = true;
8739             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8740             if(this.monitorTab){
8741                 this.el.on("keydown", this.checkTab, this);
8742             }
8743         }
8744         */
8745     },
8746
8747     // private
8748     checkTab : function(e){
8749         if(e.getKey() == e.TAB){
8750             this.triggerBlur();
8751         }
8752     },
8753
8754     // private
8755     onBlur : function(){
8756         // do nothing
8757     },
8758
8759     // private
8760     mimicBlur : function(e, t){
8761         /*
8762         if(!this.wrap.contains(t) && this.validateBlur()){
8763             this.triggerBlur();
8764         }
8765         */
8766     },
8767
8768     // private
8769     triggerBlur : function(){
8770         this.mimicing = false;
8771         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8772         if(this.monitorTab){
8773             this.el.un("keydown", this.checkTab, this);
8774         }
8775         //this.wrap.removeClass('x-trigger-wrap-focus');
8776         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8777     },
8778
8779     // private
8780     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8781     validateBlur : function(e, t){
8782         return true;
8783     },
8784
8785     // private
8786     onDisable : function(){
8787         this.inputEl().dom.disabled = true;
8788         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8789         //if(this.wrap){
8790         //    this.wrap.addClass('x-item-disabled');
8791         //}
8792     },
8793
8794     // private
8795     onEnable : function(){
8796         this.inputEl().dom.disabled = false;
8797         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8798         //if(this.wrap){
8799         //    this.el.removeClass('x-item-disabled');
8800         //}
8801     },
8802
8803     // private
8804     onShow : function(){
8805         var ae = this.getActionEl();
8806         
8807         if(ae){
8808             ae.dom.style.display = '';
8809             ae.dom.style.visibility = 'visible';
8810         }
8811     },
8812
8813     // private
8814     
8815     onHide : function(){
8816         var ae = this.getActionEl();
8817         ae.dom.style.display = 'none';
8818     },
8819
8820     /**
8821      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8822      * by an implementing function.
8823      * @method
8824      * @param {EventObject} e
8825      */
8826     onTriggerClick : Roo.emptyFn
8827 });
8828  /*
8829  * Based on:
8830  * Ext JS Library 1.1.1
8831  * Copyright(c) 2006-2007, Ext JS, LLC.
8832  *
8833  * Originally Released Under LGPL - original licence link has changed is not relivant.
8834  *
8835  * Fork - LGPL
8836  * <script type="text/javascript">
8837  */
8838
8839
8840 /**
8841  * @class Roo.data.SortTypes
8842  * @singleton
8843  * Defines the default sorting (casting?) comparison functions used when sorting data.
8844  */
8845 Roo.data.SortTypes = {
8846     /**
8847      * Default sort that does nothing
8848      * @param {Mixed} s The value being converted
8849      * @return {Mixed} The comparison value
8850      */
8851     none : function(s){
8852         return s;
8853     },
8854     
8855     /**
8856      * The regular expression used to strip tags
8857      * @type {RegExp}
8858      * @property
8859      */
8860     stripTagsRE : /<\/?[^>]+>/gi,
8861     
8862     /**
8863      * Strips all HTML tags to sort on text only
8864      * @param {Mixed} s The value being converted
8865      * @return {String} The comparison value
8866      */
8867     asText : function(s){
8868         return String(s).replace(this.stripTagsRE, "");
8869     },
8870     
8871     /**
8872      * Strips all HTML tags to sort on text only - Case insensitive
8873      * @param {Mixed} s The value being converted
8874      * @return {String} The comparison value
8875      */
8876     asUCText : function(s){
8877         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8878     },
8879     
8880     /**
8881      * Case insensitive string
8882      * @param {Mixed} s The value being converted
8883      * @return {String} The comparison value
8884      */
8885     asUCString : function(s) {
8886         return String(s).toUpperCase();
8887     },
8888     
8889     /**
8890      * Date sorting
8891      * @param {Mixed} s The value being converted
8892      * @return {Number} The comparison value
8893      */
8894     asDate : function(s) {
8895         if(!s){
8896             return 0;
8897         }
8898         if(s instanceof Date){
8899             return s.getTime();
8900         }
8901         return Date.parse(String(s));
8902     },
8903     
8904     /**
8905      * Float sorting
8906      * @param {Mixed} s The value being converted
8907      * @return {Float} The comparison value
8908      */
8909     asFloat : function(s) {
8910         var val = parseFloat(String(s).replace(/,/g, ""));
8911         if(isNaN(val)) val = 0;
8912         return val;
8913     },
8914     
8915     /**
8916      * Integer sorting
8917      * @param {Mixed} s The value being converted
8918      * @return {Number} The comparison value
8919      */
8920     asInt : function(s) {
8921         var val = parseInt(String(s).replace(/,/g, ""));
8922         if(isNaN(val)) val = 0;
8923         return val;
8924     }
8925 };/*
8926  * Based on:
8927  * Ext JS Library 1.1.1
8928  * Copyright(c) 2006-2007, Ext JS, LLC.
8929  *
8930  * Originally Released Under LGPL - original licence link has changed is not relivant.
8931  *
8932  * Fork - LGPL
8933  * <script type="text/javascript">
8934  */
8935
8936 /**
8937 * @class Roo.data.Record
8938  * Instances of this class encapsulate both record <em>definition</em> information, and record
8939  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8940  * to access Records cached in an {@link Roo.data.Store} object.<br>
8941  * <p>
8942  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8943  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8944  * objects.<br>
8945  * <p>
8946  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8947  * @constructor
8948  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8949  * {@link #create}. The parameters are the same.
8950  * @param {Array} data An associative Array of data values keyed by the field name.
8951  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8952  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8953  * not specified an integer id is generated.
8954  */
8955 Roo.data.Record = function(data, id){
8956     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8957     this.data = data;
8958 };
8959
8960 /**
8961  * Generate a constructor for a specific record layout.
8962  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8963  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8964  * Each field definition object may contain the following properties: <ul>
8965  * <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,
8966  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8967  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8968  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8969  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8970  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8971  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8972  * this may be omitted.</p></li>
8973  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8974  * <ul><li>auto (Default, implies no conversion)</li>
8975  * <li>string</li>
8976  * <li>int</li>
8977  * <li>float</li>
8978  * <li>boolean</li>
8979  * <li>date</li></ul></p></li>
8980  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8981  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8982  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8983  * by the Reader into an object that will be stored in the Record. It is passed the
8984  * following parameters:<ul>
8985  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8986  * </ul></p></li>
8987  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8988  * </ul>
8989  * <br>usage:<br><pre><code>
8990 var TopicRecord = Roo.data.Record.create(
8991     {name: 'title', mapping: 'topic_title'},
8992     {name: 'author', mapping: 'username'},
8993     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8994     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8995     {name: 'lastPoster', mapping: 'user2'},
8996     {name: 'excerpt', mapping: 'post_text'}
8997 );
8998
8999 var myNewRecord = new TopicRecord({
9000     title: 'Do my job please',
9001     author: 'noobie',
9002     totalPosts: 1,
9003     lastPost: new Date(),
9004     lastPoster: 'Animal',
9005     excerpt: 'No way dude!'
9006 });
9007 myStore.add(myNewRecord);
9008 </code></pre>
9009  * @method create
9010  * @static
9011  */
9012 Roo.data.Record.create = function(o){
9013     var f = function(){
9014         f.superclass.constructor.apply(this, arguments);
9015     };
9016     Roo.extend(f, Roo.data.Record);
9017     var p = f.prototype;
9018     p.fields = new Roo.util.MixedCollection(false, function(field){
9019         return field.name;
9020     });
9021     for(var i = 0, len = o.length; i < len; i++){
9022         p.fields.add(new Roo.data.Field(o[i]));
9023     }
9024     f.getField = function(name){
9025         return p.fields.get(name);  
9026     };
9027     return f;
9028 };
9029
9030 Roo.data.Record.AUTO_ID = 1000;
9031 Roo.data.Record.EDIT = 'edit';
9032 Roo.data.Record.REJECT = 'reject';
9033 Roo.data.Record.COMMIT = 'commit';
9034
9035 Roo.data.Record.prototype = {
9036     /**
9037      * Readonly flag - true if this record has been modified.
9038      * @type Boolean
9039      */
9040     dirty : false,
9041     editing : false,
9042     error: null,
9043     modified: null,
9044
9045     // private
9046     join : function(store){
9047         this.store = store;
9048     },
9049
9050     /**
9051      * Set the named field to the specified value.
9052      * @param {String} name The name of the field to set.
9053      * @param {Object} value The value to set the field to.
9054      */
9055     set : function(name, value){
9056         if(this.data[name] == value){
9057             return;
9058         }
9059         this.dirty = true;
9060         if(!this.modified){
9061             this.modified = {};
9062         }
9063         if(typeof this.modified[name] == 'undefined'){
9064             this.modified[name] = this.data[name];
9065         }
9066         this.data[name] = value;
9067         if(!this.editing && this.store){
9068             this.store.afterEdit(this);
9069         }       
9070     },
9071
9072     /**
9073      * Get the value of the named field.
9074      * @param {String} name The name of the field to get the value of.
9075      * @return {Object} The value of the field.
9076      */
9077     get : function(name){
9078         return this.data[name]; 
9079     },
9080
9081     // private
9082     beginEdit : function(){
9083         this.editing = true;
9084         this.modified = {}; 
9085     },
9086
9087     // private
9088     cancelEdit : function(){
9089         this.editing = false;
9090         delete this.modified;
9091     },
9092
9093     // private
9094     endEdit : function(){
9095         this.editing = false;
9096         if(this.dirty && this.store){
9097             this.store.afterEdit(this);
9098         }
9099     },
9100
9101     /**
9102      * Usually called by the {@link Roo.data.Store} which owns the Record.
9103      * Rejects all changes made to the Record since either creation, or the last commit operation.
9104      * Modified fields are reverted to their original values.
9105      * <p>
9106      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9107      * of reject operations.
9108      */
9109     reject : function(){
9110         var m = this.modified;
9111         for(var n in m){
9112             if(typeof m[n] != "function"){
9113                 this.data[n] = m[n];
9114             }
9115         }
9116         this.dirty = false;
9117         delete this.modified;
9118         this.editing = false;
9119         if(this.store){
9120             this.store.afterReject(this);
9121         }
9122     },
9123
9124     /**
9125      * Usually called by the {@link Roo.data.Store} which owns the Record.
9126      * Commits all changes made to the Record since either creation, or the last commit operation.
9127      * <p>
9128      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9129      * of commit operations.
9130      */
9131     commit : function(){
9132         this.dirty = false;
9133         delete this.modified;
9134         this.editing = false;
9135         if(this.store){
9136             this.store.afterCommit(this);
9137         }
9138     },
9139
9140     // private
9141     hasError : function(){
9142         return this.error != null;
9143     },
9144
9145     // private
9146     clearError : function(){
9147         this.error = null;
9148     },
9149
9150     /**
9151      * Creates a copy of this record.
9152      * @param {String} id (optional) A new record id if you don't want to use this record's id
9153      * @return {Record}
9154      */
9155     copy : function(newId) {
9156         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9157     }
9158 };/*
9159  * Based on:
9160  * Ext JS Library 1.1.1
9161  * Copyright(c) 2006-2007, Ext JS, LLC.
9162  *
9163  * Originally Released Under LGPL - original licence link has changed is not relivant.
9164  *
9165  * Fork - LGPL
9166  * <script type="text/javascript">
9167  */
9168
9169
9170
9171 /**
9172  * @class Roo.data.Store
9173  * @extends Roo.util.Observable
9174  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9175  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9176  * <p>
9177  * 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
9178  * has no knowledge of the format of the data returned by the Proxy.<br>
9179  * <p>
9180  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9181  * instances from the data object. These records are cached and made available through accessor functions.
9182  * @constructor
9183  * Creates a new Store.
9184  * @param {Object} config A config object containing the objects needed for the Store to access data,
9185  * and read the data into Records.
9186  */
9187 Roo.data.Store = function(config){
9188     this.data = new Roo.util.MixedCollection(false);
9189     this.data.getKey = function(o){
9190         return o.id;
9191     };
9192     this.baseParams = {};
9193     // private
9194     this.paramNames = {
9195         "start" : "start",
9196         "limit" : "limit",
9197         "sort" : "sort",
9198         "dir" : "dir",
9199         "multisort" : "_multisort"
9200     };
9201
9202     if(config && config.data){
9203         this.inlineData = config.data;
9204         delete config.data;
9205     }
9206
9207     Roo.apply(this, config);
9208     
9209     if(this.reader){ // reader passed
9210         this.reader = Roo.factory(this.reader, Roo.data);
9211         this.reader.xmodule = this.xmodule || false;
9212         if(!this.recordType){
9213             this.recordType = this.reader.recordType;
9214         }
9215         if(this.reader.onMetaChange){
9216             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9217         }
9218     }
9219
9220     if(this.recordType){
9221         this.fields = this.recordType.prototype.fields;
9222     }
9223     this.modified = [];
9224
9225     this.addEvents({
9226         /**
9227          * @event datachanged
9228          * Fires when the data cache has changed, and a widget which is using this Store
9229          * as a Record cache should refresh its view.
9230          * @param {Store} this
9231          */
9232         datachanged : true,
9233         /**
9234          * @event metachange
9235          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9236          * @param {Store} this
9237          * @param {Object} meta The JSON metadata
9238          */
9239         metachange : true,
9240         /**
9241          * @event add
9242          * Fires when Records have been added to the Store
9243          * @param {Store} this
9244          * @param {Roo.data.Record[]} records The array of Records added
9245          * @param {Number} index The index at which the record(s) were added
9246          */
9247         add : true,
9248         /**
9249          * @event remove
9250          * Fires when a Record has been removed from the Store
9251          * @param {Store} this
9252          * @param {Roo.data.Record} record The Record that was removed
9253          * @param {Number} index The index at which the record was removed
9254          */
9255         remove : true,
9256         /**
9257          * @event update
9258          * Fires when a Record has been updated
9259          * @param {Store} this
9260          * @param {Roo.data.Record} record The Record that was updated
9261          * @param {String} operation The update operation being performed.  Value may be one of:
9262          * <pre><code>
9263  Roo.data.Record.EDIT
9264  Roo.data.Record.REJECT
9265  Roo.data.Record.COMMIT
9266          * </code></pre>
9267          */
9268         update : true,
9269         /**
9270          * @event clear
9271          * Fires when the data cache has been cleared.
9272          * @param {Store} this
9273          */
9274         clear : true,
9275         /**
9276          * @event beforeload
9277          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9278          * the load action will be canceled.
9279          * @param {Store} this
9280          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9281          */
9282         beforeload : true,
9283         /**
9284          * @event beforeloadadd
9285          * Fires after a new set of Records has been loaded.
9286          * @param {Store} this
9287          * @param {Roo.data.Record[]} records The Records that were loaded
9288          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9289          */
9290         beforeloadadd : true,
9291         /**
9292          * @event load
9293          * Fires after a new set of Records has been loaded, before they are added to the store.
9294          * @param {Store} this
9295          * @param {Roo.data.Record[]} records The Records that were loaded
9296          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9297          * @params {Object} return from reader
9298          */
9299         load : true,
9300         /**
9301          * @event loadexception
9302          * Fires if an exception occurs in the Proxy during loading.
9303          * Called with the signature of the Proxy's "loadexception" event.
9304          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9305          * 
9306          * @param {Proxy} 
9307          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9308          * @param {Object} load options 
9309          * @param {Object} jsonData from your request (normally this contains the Exception)
9310          */
9311         loadexception : true
9312     });
9313     
9314     if(this.proxy){
9315         this.proxy = Roo.factory(this.proxy, Roo.data);
9316         this.proxy.xmodule = this.xmodule || false;
9317         this.relayEvents(this.proxy,  ["loadexception"]);
9318     }
9319     this.sortToggle = {};
9320     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9321
9322     Roo.data.Store.superclass.constructor.call(this);
9323
9324     if(this.inlineData){
9325         this.loadData(this.inlineData);
9326         delete this.inlineData;
9327     }
9328 };
9329
9330 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9331      /**
9332     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9333     * without a remote query - used by combo/forms at present.
9334     */
9335     
9336     /**
9337     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9338     */
9339     /**
9340     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9341     */
9342     /**
9343     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9344     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9345     */
9346     /**
9347     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9348     * on any HTTP request
9349     */
9350     /**
9351     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9352     */
9353     /**
9354     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9355     */
9356     multiSort: false,
9357     /**
9358     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9359     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9360     */
9361     remoteSort : false,
9362
9363     /**
9364     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9365      * loaded or when a record is removed. (defaults to false).
9366     */
9367     pruneModifiedRecords : false,
9368
9369     // private
9370     lastOptions : null,
9371
9372     /**
9373      * Add Records to the Store and fires the add event.
9374      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9375      */
9376     add : function(records){
9377         records = [].concat(records);
9378         for(var i = 0, len = records.length; i < len; i++){
9379             records[i].join(this);
9380         }
9381         var index = this.data.length;
9382         this.data.addAll(records);
9383         this.fireEvent("add", this, records, index);
9384     },
9385
9386     /**
9387      * Remove a Record from the Store and fires the remove event.
9388      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9389      */
9390     remove : function(record){
9391         var index = this.data.indexOf(record);
9392         this.data.removeAt(index);
9393         if(this.pruneModifiedRecords){
9394             this.modified.remove(record);
9395         }
9396         this.fireEvent("remove", this, record, index);
9397     },
9398
9399     /**
9400      * Remove all Records from the Store and fires the clear event.
9401      */
9402     removeAll : function(){
9403         this.data.clear();
9404         if(this.pruneModifiedRecords){
9405             this.modified = [];
9406         }
9407         this.fireEvent("clear", this);
9408     },
9409
9410     /**
9411      * Inserts Records to the Store at the given index and fires the add event.
9412      * @param {Number} index The start index at which to insert the passed Records.
9413      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9414      */
9415     insert : function(index, records){
9416         records = [].concat(records);
9417         for(var i = 0, len = records.length; i < len; i++){
9418             this.data.insert(index, records[i]);
9419             records[i].join(this);
9420         }
9421         this.fireEvent("add", this, records, index);
9422     },
9423
9424     /**
9425      * Get the index within the cache of the passed Record.
9426      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9427      * @return {Number} The index of the passed Record. Returns -1 if not found.
9428      */
9429     indexOf : function(record){
9430         return this.data.indexOf(record);
9431     },
9432
9433     /**
9434      * Get the index within the cache of the Record with the passed id.
9435      * @param {String} id The id of the Record to find.
9436      * @return {Number} The index of the Record. Returns -1 if not found.
9437      */
9438     indexOfId : function(id){
9439         return this.data.indexOfKey(id);
9440     },
9441
9442     /**
9443      * Get the Record with the specified id.
9444      * @param {String} id The id of the Record to find.
9445      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9446      */
9447     getById : function(id){
9448         return this.data.key(id);
9449     },
9450
9451     /**
9452      * Get the Record at the specified index.
9453      * @param {Number} index The index of the Record to find.
9454      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9455      */
9456     getAt : function(index){
9457         return this.data.itemAt(index);
9458     },
9459
9460     /**
9461      * Returns a range of Records between specified indices.
9462      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9463      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9464      * @return {Roo.data.Record[]} An array of Records
9465      */
9466     getRange : function(start, end){
9467         return this.data.getRange(start, end);
9468     },
9469
9470     // private
9471     storeOptions : function(o){
9472         o = Roo.apply({}, o);
9473         delete o.callback;
9474         delete o.scope;
9475         this.lastOptions = o;
9476     },
9477
9478     /**
9479      * Loads the Record cache from the configured Proxy using the configured Reader.
9480      * <p>
9481      * If using remote paging, then the first load call must specify the <em>start</em>
9482      * and <em>limit</em> properties in the options.params property to establish the initial
9483      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9484      * <p>
9485      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9486      * and this call will return before the new data has been loaded. Perform any post-processing
9487      * in a callback function, or in a "load" event handler.</strong>
9488      * <p>
9489      * @param {Object} options An object containing properties which control loading options:<ul>
9490      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9491      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9492      * passed the following arguments:<ul>
9493      * <li>r : Roo.data.Record[]</li>
9494      * <li>options: Options object from the load call</li>
9495      * <li>success: Boolean success indicator</li></ul></li>
9496      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9497      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9498      * </ul>
9499      */
9500     load : function(options){
9501         options = options || {};
9502         if(this.fireEvent("beforeload", this, options) !== false){
9503             this.storeOptions(options);
9504             var p = Roo.apply(options.params || {}, this.baseParams);
9505             // if meta was not loaded from remote source.. try requesting it.
9506             if (!this.reader.metaFromRemote) {
9507                 p._requestMeta = 1;
9508             }
9509             if(this.sortInfo && this.remoteSort){
9510                 var pn = this.paramNames;
9511                 p[pn["sort"]] = this.sortInfo.field;
9512                 p[pn["dir"]] = this.sortInfo.direction;
9513             }
9514             if (this.multiSort) {
9515                 var pn = this.paramNames;
9516                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9517             }
9518             
9519             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9520         }
9521     },
9522
9523     /**
9524      * Reloads the Record cache from the configured Proxy using the configured Reader and
9525      * the options from the last load operation performed.
9526      * @param {Object} options (optional) An object containing properties which may override the options
9527      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9528      * the most recently used options are reused).
9529      */
9530     reload : function(options){
9531         this.load(Roo.applyIf(options||{}, this.lastOptions));
9532     },
9533
9534     // private
9535     // Called as a callback by the Reader during a load operation.
9536     loadRecords : function(o, options, success){
9537         if(!o || success === false){
9538             if(success !== false){
9539                 this.fireEvent("load", this, [], options, o);
9540             }
9541             if(options.callback){
9542                 options.callback.call(options.scope || this, [], options, false);
9543             }
9544             return;
9545         }
9546         // if data returned failure - throw an exception.
9547         if (o.success === false) {
9548             // show a message if no listener is registered.
9549             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9550                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9551             }
9552             // loadmask wil be hooked into this..
9553             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9554             return;
9555         }
9556         var r = o.records, t = o.totalRecords || r.length;
9557         
9558         this.fireEvent("beforeloadadd", this, r, options, o);
9559         
9560         if(!options || options.add !== true){
9561             if(this.pruneModifiedRecords){
9562                 this.modified = [];
9563             }
9564             for(var i = 0, len = r.length; i < len; i++){
9565                 r[i].join(this);
9566             }
9567             if(this.snapshot){
9568                 this.data = this.snapshot;
9569                 delete this.snapshot;
9570             }
9571             this.data.clear();
9572             this.data.addAll(r);
9573             this.totalLength = t;
9574             this.applySort();
9575             this.fireEvent("datachanged", this);
9576         }else{
9577             this.totalLength = Math.max(t, this.data.length+r.length);
9578             this.add(r);
9579         }
9580         this.fireEvent("load", this, r, options, o);
9581         if(options.callback){
9582             options.callback.call(options.scope || this, r, options, true);
9583         }
9584     },
9585
9586
9587     /**
9588      * Loads data from a passed data block. A Reader which understands the format of the data
9589      * must have been configured in the constructor.
9590      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9591      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9592      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9593      */
9594     loadData : function(o, append){
9595         var r = this.reader.readRecords(o);
9596         this.loadRecords(r, {add: append}, true);
9597     },
9598
9599     /**
9600      * Gets the number of cached records.
9601      * <p>
9602      * <em>If using paging, this may not be the total size of the dataset. If the data object
9603      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9604      * the data set size</em>
9605      */
9606     getCount : function(){
9607         return this.data.length || 0;
9608     },
9609
9610     /**
9611      * Gets the total number of records in the dataset as returned by the server.
9612      * <p>
9613      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9614      * the dataset size</em>
9615      */
9616     getTotalCount : function(){
9617         return this.totalLength || 0;
9618     },
9619
9620     /**
9621      * Returns the sort state of the Store as an object with two properties:
9622      * <pre><code>
9623  field {String} The name of the field by which the Records are sorted
9624  direction {String} The sort order, "ASC" or "DESC"
9625      * </code></pre>
9626      */
9627     getSortState : function(){
9628         return this.sortInfo;
9629     },
9630
9631     // private
9632     applySort : function(){
9633         if(this.sortInfo && !this.remoteSort){
9634             var s = this.sortInfo, f = s.field;
9635             var st = this.fields.get(f).sortType;
9636             var fn = function(r1, r2){
9637                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9638                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9639             };
9640             this.data.sort(s.direction, fn);
9641             if(this.snapshot && this.snapshot != this.data){
9642                 this.snapshot.sort(s.direction, fn);
9643             }
9644         }
9645     },
9646
9647     /**
9648      * Sets the default sort column and order to be used by the next load operation.
9649      * @param {String} fieldName The name of the field to sort by.
9650      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9651      */
9652     setDefaultSort : function(field, dir){
9653         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9654     },
9655
9656     /**
9657      * Sort the Records.
9658      * If remote sorting is used, the sort is performed on the server, and the cache is
9659      * reloaded. If local sorting is used, the cache is sorted internally.
9660      * @param {String} fieldName The name of the field to sort by.
9661      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9662      */
9663     sort : function(fieldName, dir){
9664         var f = this.fields.get(fieldName);
9665         if(!dir){
9666             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9667             
9668             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9669                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9670             }else{
9671                 dir = f.sortDir;
9672             }
9673         }
9674         this.sortToggle[f.name] = dir;
9675         this.sortInfo = {field: f.name, direction: dir};
9676         if(!this.remoteSort){
9677             this.applySort();
9678             this.fireEvent("datachanged", this);
9679         }else{
9680             this.load(this.lastOptions);
9681         }
9682     },
9683
9684     /**
9685      * Calls the specified function for each of the Records in the cache.
9686      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9687      * Returning <em>false</em> aborts and exits the iteration.
9688      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9689      */
9690     each : function(fn, scope){
9691         this.data.each(fn, scope);
9692     },
9693
9694     /**
9695      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9696      * (e.g., during paging).
9697      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9698      */
9699     getModifiedRecords : function(){
9700         return this.modified;
9701     },
9702
9703     // private
9704     createFilterFn : function(property, value, anyMatch){
9705         if(!value.exec){ // not a regex
9706             value = String(value);
9707             if(value.length == 0){
9708                 return false;
9709             }
9710             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9711         }
9712         return function(r){
9713             return value.test(r.data[property]);
9714         };
9715     },
9716
9717     /**
9718      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9719      * @param {String} property A field on your records
9720      * @param {Number} start The record index to start at (defaults to 0)
9721      * @param {Number} end The last record index to include (defaults to length - 1)
9722      * @return {Number} The sum
9723      */
9724     sum : function(property, start, end){
9725         var rs = this.data.items, v = 0;
9726         start = start || 0;
9727         end = (end || end === 0) ? end : rs.length-1;
9728
9729         for(var i = start; i <= end; i++){
9730             v += (rs[i].data[property] || 0);
9731         }
9732         return v;
9733     },
9734
9735     /**
9736      * Filter the records by a specified property.
9737      * @param {String} field A field on your records
9738      * @param {String/RegExp} value Either a string that the field
9739      * should start with or a RegExp to test against the field
9740      * @param {Boolean} anyMatch True to match any part not just the beginning
9741      */
9742     filter : function(property, value, anyMatch){
9743         var fn = this.createFilterFn(property, value, anyMatch);
9744         return fn ? this.filterBy(fn) : this.clearFilter();
9745     },
9746
9747     /**
9748      * Filter by a function. The specified function will be called with each
9749      * record in this data source. If the function returns true the record is included,
9750      * otherwise it is filtered.
9751      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9752      * @param {Object} scope (optional) The scope of the function (defaults to this)
9753      */
9754     filterBy : function(fn, scope){
9755         this.snapshot = this.snapshot || this.data;
9756         this.data = this.queryBy(fn, scope||this);
9757         this.fireEvent("datachanged", this);
9758     },
9759
9760     /**
9761      * Query the records by a specified property.
9762      * @param {String} field A field on your records
9763      * @param {String/RegExp} value Either a string that the field
9764      * should start with or a RegExp to test against the field
9765      * @param {Boolean} anyMatch True to match any part not just the beginning
9766      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9767      */
9768     query : function(property, value, anyMatch){
9769         var fn = this.createFilterFn(property, value, anyMatch);
9770         return fn ? this.queryBy(fn) : this.data.clone();
9771     },
9772
9773     /**
9774      * Query by a function. The specified function will be called with each
9775      * record in this data source. If the function returns true the record is included
9776      * in the results.
9777      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9778      * @param {Object} scope (optional) The scope of the function (defaults to this)
9779       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9780      **/
9781     queryBy : function(fn, scope){
9782         var data = this.snapshot || this.data;
9783         return data.filterBy(fn, scope||this);
9784     },
9785
9786     /**
9787      * Collects unique values for a particular dataIndex from this store.
9788      * @param {String} dataIndex The property to collect
9789      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9790      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9791      * @return {Array} An array of the unique values
9792      **/
9793     collect : function(dataIndex, allowNull, bypassFilter){
9794         var d = (bypassFilter === true && this.snapshot) ?
9795                 this.snapshot.items : this.data.items;
9796         var v, sv, r = [], l = {};
9797         for(var i = 0, len = d.length; i < len; i++){
9798             v = d[i].data[dataIndex];
9799             sv = String(v);
9800             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9801                 l[sv] = true;
9802                 r[r.length] = v;
9803             }
9804         }
9805         return r;
9806     },
9807
9808     /**
9809      * Revert to a view of the Record cache with no filtering applied.
9810      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9811      */
9812     clearFilter : function(suppressEvent){
9813         if(this.snapshot && this.snapshot != this.data){
9814             this.data = this.snapshot;
9815             delete this.snapshot;
9816             if(suppressEvent !== true){
9817                 this.fireEvent("datachanged", this);
9818             }
9819         }
9820     },
9821
9822     // private
9823     afterEdit : function(record){
9824         if(this.modified.indexOf(record) == -1){
9825             this.modified.push(record);
9826         }
9827         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9828     },
9829     
9830     // private
9831     afterReject : function(record){
9832         this.modified.remove(record);
9833         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9834     },
9835
9836     // private
9837     afterCommit : function(record){
9838         this.modified.remove(record);
9839         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9840     },
9841
9842     /**
9843      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9844      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9845      */
9846     commitChanges : function(){
9847         var m = this.modified.slice(0);
9848         this.modified = [];
9849         for(var i = 0, len = m.length; i < len; i++){
9850             m[i].commit();
9851         }
9852     },
9853
9854     /**
9855      * Cancel outstanding changes on all changed records.
9856      */
9857     rejectChanges : function(){
9858         var m = this.modified.slice(0);
9859         this.modified = [];
9860         for(var i = 0, len = m.length; i < len; i++){
9861             m[i].reject();
9862         }
9863     },
9864
9865     onMetaChange : function(meta, rtype, o){
9866         this.recordType = rtype;
9867         this.fields = rtype.prototype.fields;
9868         delete this.snapshot;
9869         this.sortInfo = meta.sortInfo || this.sortInfo;
9870         this.modified = [];
9871         this.fireEvent('metachange', this, this.reader.meta);
9872     },
9873     
9874     moveIndex : function(data, type)
9875     {
9876         var index = this.indexOf(data);
9877         
9878         var newIndex = index + type;
9879         
9880         this.remove(data);
9881         
9882         this.insert(newIndex, data);
9883         
9884     }
9885 });/*
9886  * Based on:
9887  * Ext JS Library 1.1.1
9888  * Copyright(c) 2006-2007, Ext JS, LLC.
9889  *
9890  * Originally Released Under LGPL - original licence link has changed is not relivant.
9891  *
9892  * Fork - LGPL
9893  * <script type="text/javascript">
9894  */
9895
9896 /**
9897  * @class Roo.data.SimpleStore
9898  * @extends Roo.data.Store
9899  * Small helper class to make creating Stores from Array data easier.
9900  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9901  * @cfg {Array} fields An array of field definition objects, or field name strings.
9902  * @cfg {Array} data The multi-dimensional array of data
9903  * @constructor
9904  * @param {Object} config
9905  */
9906 Roo.data.SimpleStore = function(config){
9907     Roo.data.SimpleStore.superclass.constructor.call(this, {
9908         isLocal : true,
9909         reader: new Roo.data.ArrayReader({
9910                 id: config.id
9911             },
9912             Roo.data.Record.create(config.fields)
9913         ),
9914         proxy : new Roo.data.MemoryProxy(config.data)
9915     });
9916     this.load();
9917 };
9918 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9919  * Based on:
9920  * Ext JS Library 1.1.1
9921  * Copyright(c) 2006-2007, Ext JS, LLC.
9922  *
9923  * Originally Released Under LGPL - original licence link has changed is not relivant.
9924  *
9925  * Fork - LGPL
9926  * <script type="text/javascript">
9927  */
9928
9929 /**
9930 /**
9931  * @extends Roo.data.Store
9932  * @class Roo.data.JsonStore
9933  * Small helper class to make creating Stores for JSON data easier. <br/>
9934 <pre><code>
9935 var store = new Roo.data.JsonStore({
9936     url: 'get-images.php',
9937     root: 'images',
9938     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9939 });
9940 </code></pre>
9941  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9942  * JsonReader and HttpProxy (unless inline data is provided).</b>
9943  * @cfg {Array} fields An array of field definition objects, or field name strings.
9944  * @constructor
9945  * @param {Object} config
9946  */
9947 Roo.data.JsonStore = function(c){
9948     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9949         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9950         reader: new Roo.data.JsonReader(c, c.fields)
9951     }));
9952 };
9953 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9954  * Based on:
9955  * Ext JS Library 1.1.1
9956  * Copyright(c) 2006-2007, Ext JS, LLC.
9957  *
9958  * Originally Released Under LGPL - original licence link has changed is not relivant.
9959  *
9960  * Fork - LGPL
9961  * <script type="text/javascript">
9962  */
9963
9964  
9965 Roo.data.Field = function(config){
9966     if(typeof config == "string"){
9967         config = {name: config};
9968     }
9969     Roo.apply(this, config);
9970     
9971     if(!this.type){
9972         this.type = "auto";
9973     }
9974     
9975     var st = Roo.data.SortTypes;
9976     // named sortTypes are supported, here we look them up
9977     if(typeof this.sortType == "string"){
9978         this.sortType = st[this.sortType];
9979     }
9980     
9981     // set default sortType for strings and dates
9982     if(!this.sortType){
9983         switch(this.type){
9984             case "string":
9985                 this.sortType = st.asUCString;
9986                 break;
9987             case "date":
9988                 this.sortType = st.asDate;
9989                 break;
9990             default:
9991                 this.sortType = st.none;
9992         }
9993     }
9994
9995     // define once
9996     var stripRe = /[\$,%]/g;
9997
9998     // prebuilt conversion function for this field, instead of
9999     // switching every time we're reading a value
10000     if(!this.convert){
10001         var cv, dateFormat = this.dateFormat;
10002         switch(this.type){
10003             case "":
10004             case "auto":
10005             case undefined:
10006                 cv = function(v){ return v; };
10007                 break;
10008             case "string":
10009                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10010                 break;
10011             case "int":
10012                 cv = function(v){
10013                     return v !== undefined && v !== null && v !== '' ?
10014                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10015                     };
10016                 break;
10017             case "float":
10018                 cv = function(v){
10019                     return v !== undefined && v !== null && v !== '' ?
10020                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10021                     };
10022                 break;
10023             case "bool":
10024             case "boolean":
10025                 cv = function(v){ return v === true || v === "true" || v == 1; };
10026                 break;
10027             case "date":
10028                 cv = function(v){
10029                     if(!v){
10030                         return '';
10031                     }
10032                     if(v instanceof Date){
10033                         return v;
10034                     }
10035                     if(dateFormat){
10036                         if(dateFormat == "timestamp"){
10037                             return new Date(v*1000);
10038                         }
10039                         return Date.parseDate(v, dateFormat);
10040                     }
10041                     var parsed = Date.parse(v);
10042                     return parsed ? new Date(parsed) : null;
10043                 };
10044              break;
10045             
10046         }
10047         this.convert = cv;
10048     }
10049 };
10050
10051 Roo.data.Field.prototype = {
10052     dateFormat: null,
10053     defaultValue: "",
10054     mapping: null,
10055     sortType : null,
10056     sortDir : "ASC"
10057 };/*
10058  * Based on:
10059  * Ext JS Library 1.1.1
10060  * Copyright(c) 2006-2007, Ext JS, LLC.
10061  *
10062  * Originally Released Under LGPL - original licence link has changed is not relivant.
10063  *
10064  * Fork - LGPL
10065  * <script type="text/javascript">
10066  */
10067  
10068 // Base class for reading structured data from a data source.  This class is intended to be
10069 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10070
10071 /**
10072  * @class Roo.data.DataReader
10073  * Base class for reading structured data from a data source.  This class is intended to be
10074  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10075  */
10076
10077 Roo.data.DataReader = function(meta, recordType){
10078     
10079     this.meta = meta;
10080     
10081     this.recordType = recordType instanceof Array ? 
10082         Roo.data.Record.create(recordType) : recordType;
10083 };
10084
10085 Roo.data.DataReader.prototype = {
10086      /**
10087      * Create an empty record
10088      * @param {Object} data (optional) - overlay some values
10089      * @return {Roo.data.Record} record created.
10090      */
10091     newRow :  function(d) {
10092         var da =  {};
10093         this.recordType.prototype.fields.each(function(c) {
10094             switch( c.type) {
10095                 case 'int' : da[c.name] = 0; break;
10096                 case 'date' : da[c.name] = new Date(); break;
10097                 case 'float' : da[c.name] = 0.0; break;
10098                 case 'boolean' : da[c.name] = false; break;
10099                 default : da[c.name] = ""; break;
10100             }
10101             
10102         });
10103         return new this.recordType(Roo.apply(da, d));
10104     }
10105     
10106 };/*
10107  * Based on:
10108  * Ext JS Library 1.1.1
10109  * Copyright(c) 2006-2007, Ext JS, LLC.
10110  *
10111  * Originally Released Under LGPL - original licence link has changed is not relivant.
10112  *
10113  * Fork - LGPL
10114  * <script type="text/javascript">
10115  */
10116
10117 /**
10118  * @class Roo.data.DataProxy
10119  * @extends Roo.data.Observable
10120  * This class is an abstract base class for implementations which provide retrieval of
10121  * unformatted data objects.<br>
10122  * <p>
10123  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10124  * (of the appropriate type which knows how to parse the data object) to provide a block of
10125  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10126  * <p>
10127  * Custom implementations must implement the load method as described in
10128  * {@link Roo.data.HttpProxy#load}.
10129  */
10130 Roo.data.DataProxy = function(){
10131     this.addEvents({
10132         /**
10133          * @event beforeload
10134          * Fires before a network request is made to retrieve a data object.
10135          * @param {Object} This DataProxy object.
10136          * @param {Object} params The params parameter to the load function.
10137          */
10138         beforeload : true,
10139         /**
10140          * @event load
10141          * Fires before the load method's callback is called.
10142          * @param {Object} This DataProxy object.
10143          * @param {Object} o The data object.
10144          * @param {Object} arg The callback argument object passed to the load function.
10145          */
10146         load : true,
10147         /**
10148          * @event loadexception
10149          * Fires if an Exception occurs during data retrieval.
10150          * @param {Object} This DataProxy object.
10151          * @param {Object} o The data object.
10152          * @param {Object} arg The callback argument object passed to the load function.
10153          * @param {Object} e The Exception.
10154          */
10155         loadexception : true
10156     });
10157     Roo.data.DataProxy.superclass.constructor.call(this);
10158 };
10159
10160 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10161
10162     /**
10163      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10164      */
10165 /*
10166  * Based on:
10167  * Ext JS Library 1.1.1
10168  * Copyright(c) 2006-2007, Ext JS, LLC.
10169  *
10170  * Originally Released Under LGPL - original licence link has changed is not relivant.
10171  *
10172  * Fork - LGPL
10173  * <script type="text/javascript">
10174  */
10175 /**
10176  * @class Roo.data.MemoryProxy
10177  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10178  * to the Reader when its load method is called.
10179  * @constructor
10180  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10181  */
10182 Roo.data.MemoryProxy = function(data){
10183     if (data.data) {
10184         data = data.data;
10185     }
10186     Roo.data.MemoryProxy.superclass.constructor.call(this);
10187     this.data = data;
10188 };
10189
10190 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10191     /**
10192      * Load data from the requested source (in this case an in-memory
10193      * data object passed to the constructor), read the data object into
10194      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10195      * process that block using the passed callback.
10196      * @param {Object} params This parameter is not used by the MemoryProxy class.
10197      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10198      * object into a block of Roo.data.Records.
10199      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10200      * The function must be passed <ul>
10201      * <li>The Record block object</li>
10202      * <li>The "arg" argument from the load function</li>
10203      * <li>A boolean success indicator</li>
10204      * </ul>
10205      * @param {Object} scope The scope in which to call the callback
10206      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10207      */
10208     load : function(params, reader, callback, scope, arg){
10209         params = params || {};
10210         var result;
10211         try {
10212             result = reader.readRecords(this.data);
10213         }catch(e){
10214             this.fireEvent("loadexception", this, arg, null, e);
10215             callback.call(scope, null, arg, false);
10216             return;
10217         }
10218         callback.call(scope, result, arg, true);
10219     },
10220     
10221     // private
10222     update : function(params, records){
10223         
10224     }
10225 });/*
10226  * Based on:
10227  * Ext JS Library 1.1.1
10228  * Copyright(c) 2006-2007, Ext JS, LLC.
10229  *
10230  * Originally Released Under LGPL - original licence link has changed is not relivant.
10231  *
10232  * Fork - LGPL
10233  * <script type="text/javascript">
10234  */
10235 /**
10236  * @class Roo.data.HttpProxy
10237  * @extends Roo.data.DataProxy
10238  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10239  * configured to reference a certain URL.<br><br>
10240  * <p>
10241  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10242  * from which the running page was served.<br><br>
10243  * <p>
10244  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10245  * <p>
10246  * Be aware that to enable the browser to parse an XML document, the server must set
10247  * the Content-Type header in the HTTP response to "text/xml".
10248  * @constructor
10249  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10250  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10251  * will be used to make the request.
10252  */
10253 Roo.data.HttpProxy = function(conn){
10254     Roo.data.HttpProxy.superclass.constructor.call(this);
10255     // is conn a conn config or a real conn?
10256     this.conn = conn;
10257     this.useAjax = !conn || !conn.events;
10258   
10259 };
10260
10261 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10262     // thse are take from connection...
10263     
10264     /**
10265      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10266      */
10267     /**
10268      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10269      * extra parameters to each request made by this object. (defaults to undefined)
10270      */
10271     /**
10272      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10273      *  to each request made by this object. (defaults to undefined)
10274      */
10275     /**
10276      * @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)
10277      */
10278     /**
10279      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10280      */
10281      /**
10282      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10283      * @type Boolean
10284      */
10285   
10286
10287     /**
10288      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10289      * @type Boolean
10290      */
10291     /**
10292      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10293      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10294      * a finer-grained basis than the DataProxy events.
10295      */
10296     getConnection : function(){
10297         return this.useAjax ? Roo.Ajax : this.conn;
10298     },
10299
10300     /**
10301      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10302      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10303      * process that block using the passed callback.
10304      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10305      * for the request to the remote server.
10306      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10307      * object into a block of Roo.data.Records.
10308      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10309      * The function must be passed <ul>
10310      * <li>The Record block object</li>
10311      * <li>The "arg" argument from the load function</li>
10312      * <li>A boolean success indicator</li>
10313      * </ul>
10314      * @param {Object} scope The scope in which to call the callback
10315      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10316      */
10317     load : function(params, reader, callback, scope, arg){
10318         if(this.fireEvent("beforeload", this, params) !== false){
10319             var  o = {
10320                 params : params || {},
10321                 request: {
10322                     callback : callback,
10323                     scope : scope,
10324                     arg : arg
10325                 },
10326                 reader: reader,
10327                 callback : this.loadResponse,
10328                 scope: this
10329             };
10330             if(this.useAjax){
10331                 Roo.applyIf(o, this.conn);
10332                 if(this.activeRequest){
10333                     Roo.Ajax.abort(this.activeRequest);
10334                 }
10335                 this.activeRequest = Roo.Ajax.request(o);
10336             }else{
10337                 this.conn.request(o);
10338             }
10339         }else{
10340             callback.call(scope||this, null, arg, false);
10341         }
10342     },
10343
10344     // private
10345     loadResponse : function(o, success, response){
10346         delete this.activeRequest;
10347         if(!success){
10348             this.fireEvent("loadexception", this, o, response);
10349             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10350             return;
10351         }
10352         var result;
10353         try {
10354             result = o.reader.read(response);
10355         }catch(e){
10356             this.fireEvent("loadexception", this, o, response, e);
10357             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10358             return;
10359         }
10360         
10361         this.fireEvent("load", this, o, o.request.arg);
10362         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10363     },
10364
10365     // private
10366     update : function(dataSet){
10367
10368     },
10369
10370     // private
10371     updateResponse : function(dataSet){
10372
10373     }
10374 });/*
10375  * Based on:
10376  * Ext JS Library 1.1.1
10377  * Copyright(c) 2006-2007, Ext JS, LLC.
10378  *
10379  * Originally Released Under LGPL - original licence link has changed is not relivant.
10380  *
10381  * Fork - LGPL
10382  * <script type="text/javascript">
10383  */
10384
10385 /**
10386  * @class Roo.data.ScriptTagProxy
10387  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10388  * other than the originating domain of the running page.<br><br>
10389  * <p>
10390  * <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
10391  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10392  * <p>
10393  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10394  * source code that is used as the source inside a &lt;script> tag.<br><br>
10395  * <p>
10396  * In order for the browser to process the returned data, the server must wrap the data object
10397  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10398  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10399  * depending on whether the callback name was passed:
10400  * <p>
10401  * <pre><code>
10402 boolean scriptTag = false;
10403 String cb = request.getParameter("callback");
10404 if (cb != null) {
10405     scriptTag = true;
10406     response.setContentType("text/javascript");
10407 } else {
10408     response.setContentType("application/x-json");
10409 }
10410 Writer out = response.getWriter();
10411 if (scriptTag) {
10412     out.write(cb + "(");
10413 }
10414 out.print(dataBlock.toJsonString());
10415 if (scriptTag) {
10416     out.write(");");
10417 }
10418 </pre></code>
10419  *
10420  * @constructor
10421  * @param {Object} config A configuration object.
10422  */
10423 Roo.data.ScriptTagProxy = function(config){
10424     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10425     Roo.apply(this, config);
10426     this.head = document.getElementsByTagName("head")[0];
10427 };
10428
10429 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10430
10431 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10432     /**
10433      * @cfg {String} url The URL from which to request the data object.
10434      */
10435     /**
10436      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10437      */
10438     timeout : 30000,
10439     /**
10440      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10441      * the server the name of the callback function set up by the load call to process the returned data object.
10442      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10443      * javascript output which calls this named function passing the data object as its only parameter.
10444      */
10445     callbackParam : "callback",
10446     /**
10447      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10448      * name to the request.
10449      */
10450     nocache : true,
10451
10452     /**
10453      * Load data from the configured URL, read the data object into
10454      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10455      * process that block using the passed callback.
10456      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10457      * for the request to the remote server.
10458      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10459      * object into a block of Roo.data.Records.
10460      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10461      * The function must be passed <ul>
10462      * <li>The Record block object</li>
10463      * <li>The "arg" argument from the load function</li>
10464      * <li>A boolean success indicator</li>
10465      * </ul>
10466      * @param {Object} scope The scope in which to call the callback
10467      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10468      */
10469     load : function(params, reader, callback, scope, arg){
10470         if(this.fireEvent("beforeload", this, params) !== false){
10471
10472             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10473
10474             var url = this.url;
10475             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10476             if(this.nocache){
10477                 url += "&_dc=" + (new Date().getTime());
10478             }
10479             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10480             var trans = {
10481                 id : transId,
10482                 cb : "stcCallback"+transId,
10483                 scriptId : "stcScript"+transId,
10484                 params : params,
10485                 arg : arg,
10486                 url : url,
10487                 callback : callback,
10488                 scope : scope,
10489                 reader : reader
10490             };
10491             var conn = this;
10492
10493             window[trans.cb] = function(o){
10494                 conn.handleResponse(o, trans);
10495             };
10496
10497             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10498
10499             if(this.autoAbort !== false){
10500                 this.abort();
10501             }
10502
10503             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10504
10505             var script = document.createElement("script");
10506             script.setAttribute("src", url);
10507             script.setAttribute("type", "text/javascript");
10508             script.setAttribute("id", trans.scriptId);
10509             this.head.appendChild(script);
10510
10511             this.trans = trans;
10512         }else{
10513             callback.call(scope||this, null, arg, false);
10514         }
10515     },
10516
10517     // private
10518     isLoading : function(){
10519         return this.trans ? true : false;
10520     },
10521
10522     /**
10523      * Abort the current server request.
10524      */
10525     abort : function(){
10526         if(this.isLoading()){
10527             this.destroyTrans(this.trans);
10528         }
10529     },
10530
10531     // private
10532     destroyTrans : function(trans, isLoaded){
10533         this.head.removeChild(document.getElementById(trans.scriptId));
10534         clearTimeout(trans.timeoutId);
10535         if(isLoaded){
10536             window[trans.cb] = undefined;
10537             try{
10538                 delete window[trans.cb];
10539             }catch(e){}
10540         }else{
10541             // if hasn't been loaded, wait for load to remove it to prevent script error
10542             window[trans.cb] = function(){
10543                 window[trans.cb] = undefined;
10544                 try{
10545                     delete window[trans.cb];
10546                 }catch(e){}
10547             };
10548         }
10549     },
10550
10551     // private
10552     handleResponse : function(o, trans){
10553         this.trans = false;
10554         this.destroyTrans(trans, true);
10555         var result;
10556         try {
10557             result = trans.reader.readRecords(o);
10558         }catch(e){
10559             this.fireEvent("loadexception", this, o, trans.arg, e);
10560             trans.callback.call(trans.scope||window, null, trans.arg, false);
10561             return;
10562         }
10563         this.fireEvent("load", this, o, trans.arg);
10564         trans.callback.call(trans.scope||window, result, trans.arg, true);
10565     },
10566
10567     // private
10568     handleFailure : function(trans){
10569         this.trans = false;
10570         this.destroyTrans(trans, false);
10571         this.fireEvent("loadexception", this, null, trans.arg);
10572         trans.callback.call(trans.scope||window, null, trans.arg, false);
10573     }
10574 });/*
10575  * Based on:
10576  * Ext JS Library 1.1.1
10577  * Copyright(c) 2006-2007, Ext JS, LLC.
10578  *
10579  * Originally Released Under LGPL - original licence link has changed is not relivant.
10580  *
10581  * Fork - LGPL
10582  * <script type="text/javascript">
10583  */
10584
10585 /**
10586  * @class Roo.data.JsonReader
10587  * @extends Roo.data.DataReader
10588  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10589  * based on mappings in a provided Roo.data.Record constructor.
10590  * 
10591  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10592  * in the reply previously. 
10593  * 
10594  * <p>
10595  * Example code:
10596  * <pre><code>
10597 var RecordDef = Roo.data.Record.create([
10598     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10599     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10600 ]);
10601 var myReader = new Roo.data.JsonReader({
10602     totalProperty: "results",    // The property which contains the total dataset size (optional)
10603     root: "rows",                // The property which contains an Array of row objects
10604     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10605 }, RecordDef);
10606 </code></pre>
10607  * <p>
10608  * This would consume a JSON file like this:
10609  * <pre><code>
10610 { 'results': 2, 'rows': [
10611     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10612     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10613 }
10614 </code></pre>
10615  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10616  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10617  * paged from the remote server.
10618  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10619  * @cfg {String} root name of the property which contains the Array of row objects.
10620  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10621  * @constructor
10622  * Create a new JsonReader
10623  * @param {Object} meta Metadata configuration options
10624  * @param {Object} recordType Either an Array of field definition objects,
10625  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10626  */
10627 Roo.data.JsonReader = function(meta, recordType){
10628     
10629     meta = meta || {};
10630     // set some defaults:
10631     Roo.applyIf(meta, {
10632         totalProperty: 'total',
10633         successProperty : 'success',
10634         root : 'data',
10635         id : 'id'
10636     });
10637     
10638     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10639 };
10640 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10641     
10642     /**
10643      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10644      * Used by Store query builder to append _requestMeta to params.
10645      * 
10646      */
10647     metaFromRemote : false,
10648     /**
10649      * This method is only used by a DataProxy which has retrieved data from a remote server.
10650      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10651      * @return {Object} data A data block which is used by an Roo.data.Store object as
10652      * a cache of Roo.data.Records.
10653      */
10654     read : function(response){
10655         var json = response.responseText;
10656        
10657         var o = /* eval:var:o */ eval("("+json+")");
10658         if(!o) {
10659             throw {message: "JsonReader.read: Json object not found"};
10660         }
10661         
10662         if(o.metaData){
10663             
10664             delete this.ef;
10665             this.metaFromRemote = true;
10666             this.meta = o.metaData;
10667             this.recordType = Roo.data.Record.create(o.metaData.fields);
10668             this.onMetaChange(this.meta, this.recordType, o);
10669         }
10670         return this.readRecords(o);
10671     },
10672
10673     // private function a store will implement
10674     onMetaChange : function(meta, recordType, o){
10675
10676     },
10677
10678     /**
10679          * @ignore
10680          */
10681     simpleAccess: function(obj, subsc) {
10682         return obj[subsc];
10683     },
10684
10685         /**
10686          * @ignore
10687          */
10688     getJsonAccessor: function(){
10689         var re = /[\[\.]/;
10690         return function(expr) {
10691             try {
10692                 return(re.test(expr))
10693                     ? new Function("obj", "return obj." + expr)
10694                     : function(obj){
10695                         return obj[expr];
10696                     };
10697             } catch(e){}
10698             return Roo.emptyFn;
10699         };
10700     }(),
10701
10702     /**
10703      * Create a data block containing Roo.data.Records from an XML document.
10704      * @param {Object} o An object which contains an Array of row objects in the property specified
10705      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10706      * which contains the total size of the dataset.
10707      * @return {Object} data A data block which is used by an Roo.data.Store object as
10708      * a cache of Roo.data.Records.
10709      */
10710     readRecords : function(o){
10711         /**
10712          * After any data loads, the raw JSON data is available for further custom processing.
10713          * @type Object
10714          */
10715         this.o = o;
10716         var s = this.meta, Record = this.recordType,
10717             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10718
10719 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10720         if (!this.ef) {
10721             if(s.totalProperty) {
10722                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10723                 }
10724                 if(s.successProperty) {
10725                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10726                 }
10727                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10728                 if (s.id) {
10729                         var g = this.getJsonAccessor(s.id);
10730                         this.getId = function(rec) {
10731                                 var r = g(rec);  
10732                                 return (r === undefined || r === "") ? null : r;
10733                         };
10734                 } else {
10735                         this.getId = function(){return null;};
10736                 }
10737             this.ef = [];
10738             for(var jj = 0; jj < fl; jj++){
10739                 f = fi[jj];
10740                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10741                 this.ef[jj] = this.getJsonAccessor(map);
10742             }
10743         }
10744
10745         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10746         if(s.totalProperty){
10747             var vt = parseInt(this.getTotal(o), 10);
10748             if(!isNaN(vt)){
10749                 totalRecords = vt;
10750             }
10751         }
10752         if(s.successProperty){
10753             var vs = this.getSuccess(o);
10754             if(vs === false || vs === 'false'){
10755                 success = false;
10756             }
10757         }
10758         var records = [];
10759         for(var i = 0; i < c; i++){
10760                 var n = root[i];
10761             var values = {};
10762             var id = this.getId(n);
10763             for(var j = 0; j < fl; j++){
10764                 f = fi[j];
10765             var v = this.ef[j](n);
10766             if (!f.convert) {
10767                 Roo.log('missing convert for ' + f.name);
10768                 Roo.log(f);
10769                 continue;
10770             }
10771             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10772             }
10773             var record = new Record(values, id);
10774             record.json = n;
10775             records[i] = record;
10776         }
10777         return {
10778             raw : o,
10779             success : success,
10780             records : records,
10781             totalRecords : totalRecords
10782         };
10783     }
10784 });/*
10785  * Based on:
10786  * Ext JS Library 1.1.1
10787  * Copyright(c) 2006-2007, Ext JS, LLC.
10788  *
10789  * Originally Released Under LGPL - original licence link has changed is not relivant.
10790  *
10791  * Fork - LGPL
10792  * <script type="text/javascript">
10793  */
10794
10795 /**
10796  * @class Roo.data.ArrayReader
10797  * @extends Roo.data.DataReader
10798  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10799  * Each element of that Array represents a row of data fields. The
10800  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10801  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10802  * <p>
10803  * Example code:.
10804  * <pre><code>
10805 var RecordDef = Roo.data.Record.create([
10806     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10807     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10808 ]);
10809 var myReader = new Roo.data.ArrayReader({
10810     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10811 }, RecordDef);
10812 </code></pre>
10813  * <p>
10814  * This would consume an Array like this:
10815  * <pre><code>
10816 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10817   </code></pre>
10818  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10819  * @constructor
10820  * Create a new JsonReader
10821  * @param {Object} meta Metadata configuration options.
10822  * @param {Object} recordType Either an Array of field definition objects
10823  * as specified to {@link Roo.data.Record#create},
10824  * or an {@link Roo.data.Record} object
10825  * created using {@link Roo.data.Record#create}.
10826  */
10827 Roo.data.ArrayReader = function(meta, recordType){
10828     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10829 };
10830
10831 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10832     /**
10833      * Create a data block containing Roo.data.Records from an XML document.
10834      * @param {Object} o An Array of row objects which represents the dataset.
10835      * @return {Object} data A data block which is used by an Roo.data.Store object as
10836      * a cache of Roo.data.Records.
10837      */
10838     readRecords : function(o){
10839         var sid = this.meta ? this.meta.id : null;
10840         var recordType = this.recordType, fields = recordType.prototype.fields;
10841         var records = [];
10842         var root = o;
10843             for(var i = 0; i < root.length; i++){
10844                     var n = root[i];
10845                 var values = {};
10846                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10847                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10848                 var f = fields.items[j];
10849                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10850                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10851                 v = f.convert(v);
10852                 values[f.name] = v;
10853             }
10854                 var record = new recordType(values, id);
10855                 record.json = n;
10856                 records[records.length] = record;
10857             }
10858             return {
10859                 records : records,
10860                 totalRecords : records.length
10861             };
10862     }
10863 });/*
10864  * - LGPL
10865  * * 
10866  */
10867
10868 /**
10869  * @class Roo.bootstrap.ComboBox
10870  * @extends Roo.bootstrap.TriggerField
10871  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10872  * @cfg {Boolean} append (true|false) default false
10873  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10874  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10875  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10876  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10877  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10878  * @constructor
10879  * Create a new ComboBox.
10880  * @param {Object} config Configuration options
10881  */
10882 Roo.bootstrap.ComboBox = function(config){
10883     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10884     this.addEvents({
10885         /**
10886          * @event expand
10887          * Fires when the dropdown list is expanded
10888              * @param {Roo.bootstrap.ComboBox} combo This combo box
10889              */
10890         'expand' : true,
10891         /**
10892          * @event collapse
10893          * Fires when the dropdown list is collapsed
10894              * @param {Roo.bootstrap.ComboBox} combo This combo box
10895              */
10896         'collapse' : true,
10897         /**
10898          * @event beforeselect
10899          * Fires before a list item is selected. Return false to cancel the selection.
10900              * @param {Roo.bootstrap.ComboBox} combo This combo box
10901              * @param {Roo.data.Record} record The data record returned from the underlying store
10902              * @param {Number} index The index of the selected item in the dropdown list
10903              */
10904         'beforeselect' : true,
10905         /**
10906          * @event select
10907          * Fires when a list item is selected
10908              * @param {Roo.bootstrap.ComboBox} combo This combo box
10909              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10910              * @param {Number} index The index of the selected item in the dropdown list
10911              */
10912         'select' : true,
10913         /**
10914          * @event beforequery
10915          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10916          * The event object passed has these properties:
10917              * @param {Roo.bootstrap.ComboBox} combo This combo box
10918              * @param {String} query The query
10919              * @param {Boolean} forceAll true to force "all" query
10920              * @param {Boolean} cancel true to cancel the query
10921              * @param {Object} e The query event object
10922              */
10923         'beforequery': true,
10924          /**
10925          * @event add
10926          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10927              * @param {Roo.bootstrap.ComboBox} combo This combo box
10928              */
10929         'add' : true,
10930         /**
10931          * @event edit
10932          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10933              * @param {Roo.bootstrap.ComboBox} combo This combo box
10934              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10935              */
10936         'edit' : true,
10937         /**
10938          * @event remove
10939          * Fires when the remove value from the combobox array
10940              * @param {Roo.bootstrap.ComboBox} combo This combo box
10941              */
10942         'remove' : true,
10943         /**
10944          * @event specialfilter
10945          * Fires when specialfilter
10946             * @param {Roo.bootstrap.ComboBox} combo This combo box
10947             */
10948         'specialfilter' : true
10949         
10950     });
10951     
10952     this.item = [];
10953     this.tickItems = [];
10954     
10955     this.selectedIndex = -1;
10956     if(this.mode == 'local'){
10957         if(config.queryDelay === undefined){
10958             this.queryDelay = 10;
10959         }
10960         if(config.minChars === undefined){
10961             this.minChars = 0;
10962         }
10963     }
10964 };
10965
10966 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10967      
10968     /**
10969      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10970      * rendering into an Roo.Editor, defaults to false)
10971      */
10972     /**
10973      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10974      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10975      */
10976     /**
10977      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10978      */
10979     /**
10980      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10981      * the dropdown list (defaults to undefined, with no header element)
10982      */
10983
10984      /**
10985      * @cfg {String/Roo.Template} tpl The template to use to render the output
10986      */
10987      
10988      /**
10989      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10990      */
10991     listWidth: undefined,
10992     /**
10993      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10994      * mode = 'remote' or 'text' if mode = 'local')
10995      */
10996     displayField: undefined,
10997     
10998     /**
10999      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11000      * mode = 'remote' or 'value' if mode = 'local'). 
11001      * Note: use of a valueField requires the user make a selection
11002      * in order for a value to be mapped.
11003      */
11004     valueField: undefined,
11005     
11006     
11007     /**
11008      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11009      * field's data value (defaults to the underlying DOM element's name)
11010      */
11011     hiddenName: undefined,
11012     /**
11013      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11014      */
11015     listClass: '',
11016     /**
11017      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11018      */
11019     selectedClass: 'active',
11020     
11021     /**
11022      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11023      */
11024     shadow:'sides',
11025     /**
11026      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11027      * anchor positions (defaults to 'tl-bl')
11028      */
11029     listAlign: 'tl-bl?',
11030     /**
11031      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11032      */
11033     maxHeight: 300,
11034     /**
11035      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11036      * query specified by the allQuery config option (defaults to 'query')
11037      */
11038     triggerAction: 'query',
11039     /**
11040      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11041      * (defaults to 4, does not apply if editable = false)
11042      */
11043     minChars : 4,
11044     /**
11045      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11046      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11047      */
11048     typeAhead: false,
11049     /**
11050      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11051      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11052      */
11053     queryDelay: 500,
11054     /**
11055      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11056      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11057      */
11058     pageSize: 0,
11059     /**
11060      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11061      * when editable = true (defaults to false)
11062      */
11063     selectOnFocus:false,
11064     /**
11065      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11066      */
11067     queryParam: 'query',
11068     /**
11069      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11070      * when mode = 'remote' (defaults to 'Loading...')
11071      */
11072     loadingText: 'Loading...',
11073     /**
11074      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11075      */
11076     resizable: false,
11077     /**
11078      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11079      */
11080     handleHeight : 8,
11081     /**
11082      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11083      * traditional select (defaults to true)
11084      */
11085     editable: true,
11086     /**
11087      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11088      */
11089     allQuery: '',
11090     /**
11091      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11092      */
11093     mode: 'remote',
11094     /**
11095      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11096      * listWidth has a higher value)
11097      */
11098     minListWidth : 70,
11099     /**
11100      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11101      * allow the user to set arbitrary text into the field (defaults to false)
11102      */
11103     forceSelection:false,
11104     /**
11105      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11106      * if typeAhead = true (defaults to 250)
11107      */
11108     typeAheadDelay : 250,
11109     /**
11110      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11111      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11112      */
11113     valueNotFoundText : undefined,
11114     /**
11115      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11116      */
11117     blockFocus : false,
11118     
11119     /**
11120      * @cfg {Boolean} disableClear Disable showing of clear button.
11121      */
11122     disableClear : false,
11123     /**
11124      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11125      */
11126     alwaysQuery : false,
11127     
11128     /**
11129      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11130      */
11131     multiple : false,
11132     
11133     /**
11134      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11135      */
11136     invalidClass : "has-warning",
11137     
11138     /**
11139      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11140      */
11141     validClass : "has-success",
11142     
11143     /**
11144      * @cfg {Boolean} specialFilter (true|false) special filter default false
11145      */
11146     specialFilter : false,
11147     
11148     //private
11149     addicon : false,
11150     editicon: false,
11151     
11152     page: 0,
11153     hasQuery: false,
11154     append: false,
11155     loadNext: false,
11156     autoFocus : true,
11157     tickable : false,
11158     btnPosition : 'right',
11159     triggerList : true,
11160     showToggleBtn : true,
11161     // element that contains real text value.. (when hidden is used..)
11162     
11163     getAutoCreate : function()
11164     {
11165         var cfg = false;
11166         
11167         /*
11168          *  Normal ComboBox
11169          */
11170         if(!this.tickable){
11171             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11172             return cfg;
11173         }
11174         
11175         /*
11176          *  ComboBox with tickable selections
11177          */
11178              
11179         var align = this.labelAlign || this.parentLabelAlign();
11180         
11181         cfg = {
11182             cls : 'form-group roo-combobox-tickable' //input-group
11183         };
11184         
11185         var buttons = {
11186             tag : 'div',
11187             cls : 'tickable-buttons',
11188             cn : [
11189                 {
11190                     tag : 'button',
11191                     type : 'button',
11192                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11193                     html : 'Edit'
11194                 },
11195                 {
11196                     tag : 'button',
11197                     type : 'button',
11198                     name : 'ok',
11199                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11200                     html : 'Done'
11201                 },
11202                 {
11203                     tag : 'button',
11204                     type : 'button',
11205                     name : 'cancel',
11206                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11207                     html : 'Cancel'
11208                 }
11209             ]
11210         };
11211         
11212         if(this.editable){
11213             buttons.cn.unshift({
11214                 tag: 'input',
11215                 cls: 'select2-search-field-input'
11216             });
11217         }
11218         
11219         var _this = this;
11220         
11221         Roo.each(buttons.cn, function(c){
11222             if (_this.size) {
11223                 c.cls += ' btn-' + _this.size;
11224             }
11225
11226             if (_this.disabled) {
11227                 c.disabled = true;
11228             }
11229         });
11230         
11231         var box = {
11232             tag: 'div',
11233             cn: [
11234                 {
11235                     tag: 'input',
11236                     type : 'hidden',
11237                     cls: 'form-hidden-field'
11238                 },
11239                 {
11240                     tag: 'ul',
11241                     cls: 'select2-choices',
11242                     cn:[
11243                         {
11244                             tag: 'li',
11245                             cls: 'select2-search-field',
11246                             cn: [
11247
11248                                 buttons
11249                             ]
11250                         }
11251                     ]
11252                 }
11253             ]
11254         }
11255         
11256         var combobox = {
11257             cls: 'select2-container input-group select2-container-multi',
11258             cn: [
11259                 box
11260 //                {
11261 //                    tag: 'ul',
11262 //                    cls: 'typeahead typeahead-long dropdown-menu',
11263 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11264 //                }
11265             ]
11266         };
11267         
11268         if(this.hasFeedback && !this.allowBlank){
11269             
11270             var feedback = {
11271                 tag: 'span',
11272                 cls: 'glyphicon form-control-feedback'
11273             };
11274
11275             combobox.cn.push(feedback);
11276         }
11277         
11278         if (align ==='left' && this.fieldLabel.length) {
11279             
11280                 Roo.log("left and has label");
11281                 cfg.cn = [
11282                     
11283                     {
11284                         tag: 'label',
11285                         'for' :  id,
11286                         cls : 'control-label col-sm-' + this.labelWidth,
11287                         html : this.fieldLabel
11288                         
11289                     },
11290                     {
11291                         cls : "col-sm-" + (12 - this.labelWidth), 
11292                         cn: [
11293                             combobox
11294                         ]
11295                     }
11296                     
11297                 ];
11298         } else if ( this.fieldLabel.length) {
11299                 Roo.log(" label");
11300                  cfg.cn = [
11301                    
11302                     {
11303                         tag: 'label',
11304                         //cls : 'input-group-addon',
11305                         html : this.fieldLabel
11306                         
11307                     },
11308                     
11309                     combobox
11310                     
11311                 ];
11312
11313         } else {
11314             
11315                 Roo.log(" no label && no align");
11316                 cfg = combobox
11317                      
11318                 
11319         }
11320          
11321         var settings=this;
11322         ['xs','sm','md','lg'].map(function(size){
11323             if (settings[size]) {
11324                 cfg.cls += ' col-' + size + '-' + settings[size];
11325             }
11326         });
11327         
11328         return cfg;
11329         
11330     },
11331     
11332     // private
11333     initEvents: function()
11334     {
11335         
11336         if (!this.store) {
11337             throw "can not find store for combo";
11338         }
11339         this.store = Roo.factory(this.store, Roo.data);
11340         
11341         if(this.tickable){
11342             this.initTickableEvents();
11343             return;
11344         }
11345         
11346         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11347         
11348         if(this.hiddenName){
11349             
11350             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11351             
11352             this.hiddenField.dom.value =
11353                 this.hiddenValue !== undefined ? this.hiddenValue :
11354                 this.value !== undefined ? this.value : '';
11355
11356             // prevent input submission
11357             this.el.dom.removeAttribute('name');
11358             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11359              
11360              
11361         }
11362         //if(Roo.isGecko){
11363         //    this.el.dom.setAttribute('autocomplete', 'off');
11364         //}
11365         
11366         var cls = 'x-combo-list';
11367         
11368         //this.list = new Roo.Layer({
11369         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11370         //});
11371         
11372         var _this = this;
11373         
11374         (function(){
11375             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11376             _this.list.setWidth(lw);
11377         }).defer(100);
11378         
11379         this.list.on('mouseover', this.onViewOver, this);
11380         this.list.on('mousemove', this.onViewMove, this);
11381         
11382         this.list.on('scroll', this.onViewScroll, this);
11383         
11384         /*
11385         this.list.swallowEvent('mousewheel');
11386         this.assetHeight = 0;
11387
11388         if(this.title){
11389             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11390             this.assetHeight += this.header.getHeight();
11391         }
11392
11393         this.innerList = this.list.createChild({cls:cls+'-inner'});
11394         this.innerList.on('mouseover', this.onViewOver, this);
11395         this.innerList.on('mousemove', this.onViewMove, this);
11396         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11397         
11398         if(this.allowBlank && !this.pageSize && !this.disableClear){
11399             this.footer = this.list.createChild({cls:cls+'-ft'});
11400             this.pageTb = new Roo.Toolbar(this.footer);
11401            
11402         }
11403         if(this.pageSize){
11404             this.footer = this.list.createChild({cls:cls+'-ft'});
11405             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11406                     {pageSize: this.pageSize});
11407             
11408         }
11409         
11410         if (this.pageTb && this.allowBlank && !this.disableClear) {
11411             var _this = this;
11412             this.pageTb.add(new Roo.Toolbar.Fill(), {
11413                 cls: 'x-btn-icon x-btn-clear',
11414                 text: '&#160;',
11415                 handler: function()
11416                 {
11417                     _this.collapse();
11418                     _this.clearValue();
11419                     _this.onSelect(false, -1);
11420                 }
11421             });
11422         }
11423         if (this.footer) {
11424             this.assetHeight += this.footer.getHeight();
11425         }
11426         */
11427             
11428         if(!this.tpl){
11429             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11430         }
11431
11432         this.view = new Roo.View(this.list, this.tpl, {
11433             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11434         });
11435         //this.view.wrapEl.setDisplayed(false);
11436         this.view.on('click', this.onViewClick, this);
11437         
11438         
11439         
11440         this.store.on('beforeload', this.onBeforeLoad, this);
11441         this.store.on('load', this.onLoad, this);
11442         this.store.on('loadexception', this.onLoadException, this);
11443         /*
11444         if(this.resizable){
11445             this.resizer = new Roo.Resizable(this.list,  {
11446                pinned:true, handles:'se'
11447             });
11448             this.resizer.on('resize', function(r, w, h){
11449                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11450                 this.listWidth = w;
11451                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11452                 this.restrictHeight();
11453             }, this);
11454             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11455         }
11456         */
11457         if(!this.editable){
11458             this.editable = true;
11459             this.setEditable(false);
11460         }
11461         
11462         /*
11463         
11464         if (typeof(this.events.add.listeners) != 'undefined') {
11465             
11466             this.addicon = this.wrap.createChild(
11467                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11468        
11469             this.addicon.on('click', function(e) {
11470                 this.fireEvent('add', this);
11471             }, this);
11472         }
11473         if (typeof(this.events.edit.listeners) != 'undefined') {
11474             
11475             this.editicon = this.wrap.createChild(
11476                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11477             if (this.addicon) {
11478                 this.editicon.setStyle('margin-left', '40px');
11479             }
11480             this.editicon.on('click', function(e) {
11481                 
11482                 // we fire even  if inothing is selected..
11483                 this.fireEvent('edit', this, this.lastData );
11484                 
11485             }, this);
11486         }
11487         */
11488         
11489         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11490             "up" : function(e){
11491                 this.inKeyMode = true;
11492                 this.selectPrev();
11493             },
11494
11495             "down" : function(e){
11496                 if(!this.isExpanded()){
11497                     this.onTriggerClick();
11498                 }else{
11499                     this.inKeyMode = true;
11500                     this.selectNext();
11501                 }
11502             },
11503
11504             "enter" : function(e){
11505 //                this.onViewClick();
11506                 //return true;
11507                 this.collapse();
11508                 
11509                 if(this.fireEvent("specialkey", this, e)){
11510                     this.onViewClick(false);
11511                 }
11512                 
11513                 return true;
11514             },
11515
11516             "esc" : function(e){
11517                 this.collapse();
11518             },
11519
11520             "tab" : function(e){
11521                 this.collapse();
11522                 
11523                 if(this.fireEvent("specialkey", this, e)){
11524                     this.onViewClick(false);
11525                 }
11526                 
11527                 return true;
11528             },
11529
11530             scope : this,
11531
11532             doRelay : function(foo, bar, hname){
11533                 if(hname == 'down' || this.scope.isExpanded()){
11534                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11535                 }
11536                 return true;
11537             },
11538
11539             forceKeyDown: true
11540         });
11541         
11542         
11543         this.queryDelay = Math.max(this.queryDelay || 10,
11544                 this.mode == 'local' ? 10 : 250);
11545         
11546         
11547         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11548         
11549         if(this.typeAhead){
11550             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11551         }
11552         if(this.editable !== false){
11553             this.inputEl().on("keyup", this.onKeyUp, this);
11554         }
11555         if(this.forceSelection){
11556             this.inputEl().on('blur', this.doForce, this);
11557         }
11558         
11559         if(this.multiple){
11560             this.choices = this.el.select('ul.select2-choices', true).first();
11561             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11562         }
11563     },
11564     
11565     initTickableEvents: function()
11566     {   
11567         this.createList();
11568         
11569         if(this.hiddenName){
11570             
11571             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11572             
11573             this.hiddenField.dom.value =
11574                 this.hiddenValue !== undefined ? this.hiddenValue :
11575                 this.value !== undefined ? this.value : '';
11576
11577             // prevent input submission
11578             this.el.dom.removeAttribute('name');
11579             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11580              
11581              
11582         }
11583         
11584 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11585         
11586         this.choices = this.el.select('ul.select2-choices', true).first();
11587         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11588         if(this.triggerList){
11589             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11590         }
11591          
11592         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11593         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11594         
11595         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11596         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11597         
11598         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11599         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11600         
11601         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11602         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11603         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11604         
11605         this.okBtn.hide();
11606         this.cancelBtn.hide();
11607         
11608         var _this = this;
11609         
11610         (function(){
11611             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11612             _this.list.setWidth(lw);
11613         }).defer(100);
11614         
11615         this.list.on('mouseover', this.onViewOver, this);
11616         this.list.on('mousemove', this.onViewMove, this);
11617         
11618         this.list.on('scroll', this.onViewScroll, this);
11619         
11620         if(!this.tpl){
11621             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>';
11622         }
11623
11624         this.view = new Roo.View(this.list, this.tpl, {
11625             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11626         });
11627         
11628         //this.view.wrapEl.setDisplayed(false);
11629         this.view.on('click', this.onViewClick, this);
11630         
11631         
11632         
11633         this.store.on('beforeload', this.onBeforeLoad, this);
11634         this.store.on('load', this.onLoad, this);
11635         this.store.on('loadexception', this.onLoadException, this);
11636         
11637         if(this.editable){
11638             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11639                 "up" : function(e){
11640                     this.inKeyMode = true;
11641                     this.selectPrev();
11642                 },
11643
11644                 "down" : function(e){
11645                     this.inKeyMode = true;
11646                     this.selectNext();
11647                 },
11648
11649                 "enter" : function(e){
11650                     if(this.fireEvent("specialkey", this, e)){
11651                         this.onViewClick(false);
11652                     }
11653                     
11654                     return true;
11655                 },
11656
11657                 "esc" : function(e){
11658                     this.onTickableFooterButtonClick(e, false, false);
11659                 },
11660
11661                 "tab" : function(e){
11662                     this.fireEvent("specialkey", this, e);
11663                     
11664                     this.onTickableFooterButtonClick(e, false, false);
11665                     
11666                     return true;
11667                 },
11668
11669                 scope : this,
11670
11671                 doRelay : function(e, fn, key){
11672                     if(this.scope.isExpanded()){
11673                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11674                     }
11675                     return true;
11676                 },
11677
11678                 forceKeyDown: true
11679             });
11680         }
11681         
11682         this.queryDelay = Math.max(this.queryDelay || 10,
11683                 this.mode == 'local' ? 10 : 250);
11684         
11685         
11686         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11687         
11688         if(this.typeAhead){
11689             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11690         }
11691         
11692         if(this.editable !== false){
11693             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11694         }
11695         
11696     },
11697
11698     onDestroy : function(){
11699         if(this.view){
11700             this.view.setStore(null);
11701             this.view.el.removeAllListeners();
11702             this.view.el.remove();
11703             this.view.purgeListeners();
11704         }
11705         if(this.list){
11706             this.list.dom.innerHTML  = '';
11707         }
11708         
11709         if(this.store){
11710             this.store.un('beforeload', this.onBeforeLoad, this);
11711             this.store.un('load', this.onLoad, this);
11712             this.store.un('loadexception', this.onLoadException, this);
11713         }
11714         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11715     },
11716
11717     // private
11718     fireKey : function(e){
11719         if(e.isNavKeyPress() && !this.list.isVisible()){
11720             this.fireEvent("specialkey", this, e);
11721         }
11722     },
11723
11724     // private
11725     onResize: function(w, h){
11726 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11727 //        
11728 //        if(typeof w != 'number'){
11729 //            // we do not handle it!?!?
11730 //            return;
11731 //        }
11732 //        var tw = this.trigger.getWidth();
11733 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11734 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11735 //        var x = w - tw;
11736 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11737 //            
11738 //        //this.trigger.setStyle('left', x+'px');
11739 //        
11740 //        if(this.list && this.listWidth === undefined){
11741 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11742 //            this.list.setWidth(lw);
11743 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11744 //        }
11745         
11746     
11747         
11748     },
11749
11750     /**
11751      * Allow or prevent the user from directly editing the field text.  If false is passed,
11752      * the user will only be able to select from the items defined in the dropdown list.  This method
11753      * is the runtime equivalent of setting the 'editable' config option at config time.
11754      * @param {Boolean} value True to allow the user to directly edit the field text
11755      */
11756     setEditable : function(value){
11757         if(value == this.editable){
11758             return;
11759         }
11760         this.editable = value;
11761         if(!value){
11762             this.inputEl().dom.setAttribute('readOnly', true);
11763             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11764             this.inputEl().addClass('x-combo-noedit');
11765         }else{
11766             this.inputEl().dom.setAttribute('readOnly', false);
11767             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11768             this.inputEl().removeClass('x-combo-noedit');
11769         }
11770     },
11771
11772     // private
11773     
11774     onBeforeLoad : function(combo,opts){
11775         if(!this.hasFocus){
11776             return;
11777         }
11778          if (!opts.add) {
11779             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11780          }
11781         this.restrictHeight();
11782         this.selectedIndex = -1;
11783     },
11784
11785     // private
11786     onLoad : function(){
11787         
11788         this.hasQuery = false;
11789         
11790         if(!this.hasFocus){
11791             return;
11792         }
11793         
11794         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11795             this.loading.hide();
11796         }
11797              
11798         if(this.store.getCount() > 0){
11799             this.expand();
11800             this.restrictHeight();
11801             if(this.lastQuery == this.allQuery){
11802                 if(this.editable && !this.tickable){
11803                     this.inputEl().dom.select();
11804                 }
11805                 
11806                 if(
11807                     !this.selectByValue(this.value, true) &&
11808                     this.autoFocus && 
11809                     (
11810                         !this.store.lastOptions ||
11811                         typeof(this.store.lastOptions.add) == 'undefined' || 
11812                         this.store.lastOptions.add != true
11813                     )
11814                 ){
11815                     this.select(0, true);
11816                 }
11817             }else{
11818                 if(this.autoFocus){
11819                     this.selectNext();
11820                 }
11821                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11822                     this.taTask.delay(this.typeAheadDelay);
11823                 }
11824             }
11825         }else{
11826             this.onEmptyResults();
11827         }
11828         
11829         //this.el.focus();
11830     },
11831     // private
11832     onLoadException : function()
11833     {
11834         this.hasQuery = false;
11835         
11836         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11837             this.loading.hide();
11838         }
11839         
11840         if(this.tickable && this.editable){
11841             return;
11842         }
11843         
11844         this.collapse();
11845         
11846         Roo.log(this.store.reader.jsonData);
11847         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11848             // fixme
11849             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11850         }
11851         
11852         
11853     },
11854     // private
11855     onTypeAhead : function(){
11856         if(this.store.getCount() > 0){
11857             var r = this.store.getAt(0);
11858             var newValue = r.data[this.displayField];
11859             var len = newValue.length;
11860             var selStart = this.getRawValue().length;
11861             
11862             if(selStart != len){
11863                 this.setRawValue(newValue);
11864                 this.selectText(selStart, newValue.length);
11865             }
11866         }
11867     },
11868
11869     // private
11870     onSelect : function(record, index){
11871         
11872         if(this.fireEvent('beforeselect', this, record, index) !== false){
11873         
11874             this.setFromData(index > -1 ? record.data : false);
11875             
11876             this.collapse();
11877             this.fireEvent('select', this, record, index);
11878         }
11879     },
11880
11881     /**
11882      * Returns the currently selected field value or empty string if no value is set.
11883      * @return {String} value The selected value
11884      */
11885     getValue : function(){
11886         
11887         if(this.multiple){
11888             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11889         }
11890         
11891         if(this.valueField){
11892             return typeof this.value != 'undefined' ? this.value : '';
11893         }else{
11894             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11895         }
11896     },
11897
11898     /**
11899      * Clears any text/value currently set in the field
11900      */
11901     clearValue : function(){
11902         if(this.hiddenField){
11903             this.hiddenField.dom.value = '';
11904         }
11905         this.value = '';
11906         this.setRawValue('');
11907         this.lastSelectionText = '';
11908         this.lastData = false;
11909         
11910     },
11911
11912     /**
11913      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11914      * will be displayed in the field.  If the value does not match the data value of an existing item,
11915      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11916      * Otherwise the field will be blank (although the value will still be set).
11917      * @param {String} value The value to match
11918      */
11919     setValue : function(v){
11920         if(this.multiple){
11921             this.syncValue();
11922             return;
11923         }
11924         
11925         var text = v;
11926         if(this.valueField){
11927             var r = this.findRecord(this.valueField, v);
11928             if(r){
11929                 text = r.data[this.displayField];
11930             }else if(this.valueNotFoundText !== undefined){
11931                 text = this.valueNotFoundText;
11932             }
11933         }
11934         this.lastSelectionText = text;
11935         if(this.hiddenField){
11936             this.hiddenField.dom.value = v;
11937         }
11938         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11939         this.value = v;
11940     },
11941     /**
11942      * @property {Object} the last set data for the element
11943      */
11944     
11945     lastData : false,
11946     /**
11947      * Sets the value of the field based on a object which is related to the record format for the store.
11948      * @param {Object} value the value to set as. or false on reset?
11949      */
11950     setFromData : function(o){
11951         
11952         if(this.multiple){
11953             this.addItem(o);
11954             return;
11955         }
11956             
11957         var dv = ''; // display value
11958         var vv = ''; // value value..
11959         this.lastData = o;
11960         if (this.displayField) {
11961             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11962         } else {
11963             // this is an error condition!!!
11964             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11965         }
11966         
11967         if(this.valueField){
11968             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11969         }
11970         
11971         if(this.hiddenField){
11972             this.hiddenField.dom.value = vv;
11973             
11974             this.lastSelectionText = dv;
11975             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11976             this.value = vv;
11977             return;
11978         }
11979         // no hidden field.. - we store the value in 'value', but still display
11980         // display field!!!!
11981         this.lastSelectionText = dv;
11982         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11983         this.value = vv;
11984         
11985         
11986     },
11987     // private
11988     reset : function(){
11989         // overridden so that last data is reset..
11990         
11991         if(this.multiple){
11992             this.clearItem();
11993             return;
11994         }
11995         
11996         this.setValue(this.originalValue);
11997         this.clearInvalid();
11998         this.lastData = false;
11999         if (this.view) {
12000             this.view.clearSelections();
12001         }
12002     },
12003     // private
12004     findRecord : function(prop, value){
12005         var record;
12006         if(this.store.getCount() > 0){
12007             this.store.each(function(r){
12008                 if(r.data[prop] == value){
12009                     record = r;
12010                     return false;
12011                 }
12012                 return true;
12013             });
12014         }
12015         return record;
12016     },
12017     
12018     getName: function()
12019     {
12020         // returns hidden if it's set..
12021         if (!this.rendered) {return ''};
12022         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12023         
12024     },
12025     // private
12026     onViewMove : function(e, t){
12027         this.inKeyMode = false;
12028     },
12029
12030     // private
12031     onViewOver : function(e, t){
12032         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12033             return;
12034         }
12035         var item = this.view.findItemFromChild(t);
12036         
12037         if(item){
12038             var index = this.view.indexOf(item);
12039             this.select(index, false);
12040         }
12041     },
12042
12043     // private
12044     onViewClick : function(view, doFocus, el, e)
12045     {
12046         var index = this.view.getSelectedIndexes()[0];
12047         
12048         var r = this.store.getAt(index);
12049         
12050         if(this.tickable){
12051             
12052             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12053                 return;
12054             }
12055             
12056             var rm = false;
12057             var _this = this;
12058             
12059             Roo.each(this.tickItems, function(v,k){
12060                 
12061                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12062                     _this.tickItems.splice(k, 1);
12063                     
12064                     if(typeof(e) == 'undefined' && view == false){
12065                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12066                     }
12067                     
12068                     rm = true;
12069                     return;
12070                 }
12071             });
12072             
12073             if(rm){
12074                 return;
12075             }
12076             
12077             this.tickItems.push(r.data);
12078             
12079             if(typeof(e) == 'undefined' && view == false){
12080                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12081             }
12082                     
12083             return;
12084         }
12085         
12086         if(r){
12087             this.onSelect(r, index);
12088         }
12089         if(doFocus !== false && !this.blockFocus){
12090             this.inputEl().focus();
12091         }
12092     },
12093
12094     // private
12095     restrictHeight : function(){
12096         //this.innerList.dom.style.height = '';
12097         //var inner = this.innerList.dom;
12098         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12099         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12100         //this.list.beginUpdate();
12101         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12102         this.list.alignTo(this.inputEl(), this.listAlign);
12103         this.list.alignTo(this.inputEl(), this.listAlign);
12104         //this.list.endUpdate();
12105     },
12106
12107     // private
12108     onEmptyResults : function(){
12109         
12110         if(this.tickable && this.editable){
12111             this.restrictHeight();
12112             return;
12113         }
12114         
12115         this.collapse();
12116     },
12117
12118     /**
12119      * Returns true if the dropdown list is expanded, else false.
12120      */
12121     isExpanded : function(){
12122         return this.list.isVisible();
12123     },
12124
12125     /**
12126      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12127      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12128      * @param {String} value The data value of the item to select
12129      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12130      * selected item if it is not currently in view (defaults to true)
12131      * @return {Boolean} True if the value matched an item in the list, else false
12132      */
12133     selectByValue : function(v, scrollIntoView){
12134         if(v !== undefined && v !== null){
12135             var r = this.findRecord(this.valueField || this.displayField, v);
12136             if(r){
12137                 this.select(this.store.indexOf(r), scrollIntoView);
12138                 return true;
12139             }
12140         }
12141         return false;
12142     },
12143
12144     /**
12145      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12146      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12147      * @param {Number} index The zero-based index of the list item to select
12148      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12149      * selected item if it is not currently in view (defaults to true)
12150      */
12151     select : function(index, scrollIntoView){
12152         this.selectedIndex = index;
12153         this.view.select(index);
12154         if(scrollIntoView !== false){
12155             var el = this.view.getNode(index);
12156             /*
12157              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12158              */
12159             if(el){
12160                 this.list.scrollChildIntoView(el, false);
12161             }
12162         }
12163     },
12164
12165     // private
12166     selectNext : function(){
12167         var ct = this.store.getCount();
12168         if(ct > 0){
12169             if(this.selectedIndex == -1){
12170                 this.select(0);
12171             }else if(this.selectedIndex < ct-1){
12172                 this.select(this.selectedIndex+1);
12173             }
12174         }
12175     },
12176
12177     // private
12178     selectPrev : function(){
12179         var ct = this.store.getCount();
12180         if(ct > 0){
12181             if(this.selectedIndex == -1){
12182                 this.select(0);
12183             }else if(this.selectedIndex != 0){
12184                 this.select(this.selectedIndex-1);
12185             }
12186         }
12187     },
12188
12189     // private
12190     onKeyUp : function(e){
12191         if(this.editable !== false && !e.isSpecialKey()){
12192             this.lastKey = e.getKey();
12193             this.dqTask.delay(this.queryDelay);
12194         }
12195     },
12196
12197     // private
12198     validateBlur : function(){
12199         return !this.list || !this.list.isVisible();   
12200     },
12201
12202     // private
12203     initQuery : function(){
12204         
12205         var v = this.getRawValue();
12206         
12207         if(this.tickable && this.editable){
12208             v = this.tickableInputEl().getValue();
12209         }
12210         
12211         this.doQuery(v);
12212     },
12213
12214     // private
12215     doForce : function(){
12216         if(this.inputEl().dom.value.length > 0){
12217             this.inputEl().dom.value =
12218                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12219              
12220         }
12221     },
12222
12223     /**
12224      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12225      * query allowing the query action to be canceled if needed.
12226      * @param {String} query The SQL query to execute
12227      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12228      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12229      * saved in the current store (defaults to false)
12230      */
12231     doQuery : function(q, forceAll){
12232         
12233         if(q === undefined || q === null){
12234             q = '';
12235         }
12236         var qe = {
12237             query: q,
12238             forceAll: forceAll,
12239             combo: this,
12240             cancel:false
12241         };
12242         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12243             return false;
12244         }
12245         q = qe.query;
12246         
12247         forceAll = qe.forceAll;
12248         if(forceAll === true || (q.length >= this.minChars)){
12249             
12250             this.hasQuery = true;
12251             
12252             if(this.lastQuery != q || this.alwaysQuery){
12253                 this.lastQuery = q;
12254                 if(this.mode == 'local'){
12255                     this.selectedIndex = -1;
12256                     if(forceAll){
12257                         this.store.clearFilter();
12258                     }else{
12259                         
12260                         if(this.specialFilter){
12261                             this.fireEvent('specialfilter', this);
12262                             this.onLoad();
12263                             return;
12264                         }
12265                         
12266                         this.store.filter(this.displayField, q);
12267                     }
12268                     
12269                     this.store.fireEvent("datachanged", this.store);
12270                     
12271                     this.onLoad();
12272                     
12273                     
12274                 }else{
12275                     
12276                     this.store.baseParams[this.queryParam] = q;
12277                     
12278                     var options = {params : this.getParams(q)};
12279                     
12280                     if(this.loadNext){
12281                         options.add = true;
12282                         options.params.start = this.page * this.pageSize;
12283                     }
12284                     
12285                     this.store.load(options);
12286                     
12287                     /*
12288                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12289                      *  we should expand the list on onLoad
12290                      *  so command out it
12291                      */
12292 //                    this.expand();
12293                 }
12294             }else{
12295                 this.selectedIndex = -1;
12296                 this.onLoad();   
12297             }
12298         }
12299         
12300         this.loadNext = false;
12301     },
12302     
12303     // private
12304     getParams : function(q){
12305         var p = {};
12306         //p[this.queryParam] = q;
12307         
12308         if(this.pageSize){
12309             p.start = 0;
12310             p.limit = this.pageSize;
12311         }
12312         return p;
12313     },
12314
12315     /**
12316      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12317      */
12318     collapse : function(){
12319         if(!this.isExpanded()){
12320             return;
12321         }
12322         
12323         this.list.hide();
12324         
12325         if(this.tickable){
12326             this.hasFocus = false;
12327             this.okBtn.hide();
12328             this.cancelBtn.hide();
12329             this.trigger.show();
12330             
12331             if(this.editable){
12332                 this.tickableInputEl().dom.value = '';
12333                 this.tickableInputEl().blur();
12334             }
12335             
12336         }
12337         
12338         Roo.get(document).un('mousedown', this.collapseIf, this);
12339         Roo.get(document).un('mousewheel', this.collapseIf, this);
12340         if (!this.editable) {
12341             Roo.get(document).un('keydown', this.listKeyPress, this);
12342         }
12343         this.fireEvent('collapse', this);
12344     },
12345
12346     // private
12347     collapseIf : function(e){
12348         var in_combo  = e.within(this.el);
12349         var in_list =  e.within(this.list);
12350         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12351         
12352         if (in_combo || in_list || is_list) {
12353             //e.stopPropagation();
12354             return;
12355         }
12356         
12357         if(this.tickable){
12358             this.onTickableFooterButtonClick(e, false, false);
12359         }
12360
12361         this.collapse();
12362         
12363     },
12364
12365     /**
12366      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12367      */
12368     expand : function(){
12369        
12370         if(this.isExpanded() || !this.hasFocus){
12371             return;
12372         }
12373         
12374         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12375         this.list.setWidth(lw);
12376         
12377         
12378          Roo.log('expand');
12379         
12380         this.list.show();
12381         
12382         this.restrictHeight();
12383         
12384         if(this.tickable){
12385             
12386             this.tickItems = Roo.apply([], this.item);
12387             
12388             this.okBtn.show();
12389             this.cancelBtn.show();
12390             this.trigger.hide();
12391             
12392             if(this.editable){
12393                 this.tickableInputEl().focus();
12394             }
12395             
12396         }
12397         
12398         Roo.get(document).on('mousedown', this.collapseIf, this);
12399         Roo.get(document).on('mousewheel', this.collapseIf, this);
12400         if (!this.editable) {
12401             Roo.get(document).on('keydown', this.listKeyPress, this);
12402         }
12403         
12404         this.fireEvent('expand', this);
12405     },
12406
12407     // private
12408     // Implements the default empty TriggerField.onTriggerClick function
12409     onTriggerClick : function(e)
12410     {
12411         Roo.log('trigger click');
12412         
12413         if(this.disabled || !this.triggerList){
12414             return;
12415         }
12416         
12417         this.page = 0;
12418         this.loadNext = false;
12419         
12420         if(this.isExpanded()){
12421             this.collapse();
12422             if (!this.blockFocus) {
12423                 this.inputEl().focus();
12424             }
12425             
12426         }else {
12427             this.hasFocus = true;
12428             if(this.triggerAction == 'all') {
12429                 this.doQuery(this.allQuery, true);
12430             } else {
12431                 this.doQuery(this.getRawValue());
12432             }
12433             if (!this.blockFocus) {
12434                 this.inputEl().focus();
12435             }
12436         }
12437     },
12438     
12439     onTickableTriggerClick : function(e)
12440     {
12441         if(this.disabled){
12442             return;
12443         }
12444         
12445         this.page = 0;
12446         this.loadNext = false;
12447         this.hasFocus = true;
12448         
12449         if(this.triggerAction == 'all') {
12450             this.doQuery(this.allQuery, true);
12451         } else {
12452             this.doQuery(this.getRawValue());
12453         }
12454     },
12455     
12456     onSearchFieldClick : function(e)
12457     {
12458         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12459             this.onTickableFooterButtonClick(e, false, false);
12460             return;
12461         }
12462         
12463         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12464             return;
12465         }
12466         
12467         this.page = 0;
12468         this.loadNext = false;
12469         this.hasFocus = true;
12470         
12471         if(this.triggerAction == 'all') {
12472             this.doQuery(this.allQuery, true);
12473         } else {
12474             this.doQuery(this.getRawValue());
12475         }
12476     },
12477     
12478     listKeyPress : function(e)
12479     {
12480         //Roo.log('listkeypress');
12481         // scroll to first matching element based on key pres..
12482         if (e.isSpecialKey()) {
12483             return false;
12484         }
12485         var k = String.fromCharCode(e.getKey()).toUpperCase();
12486         //Roo.log(k);
12487         var match  = false;
12488         var csel = this.view.getSelectedNodes();
12489         var cselitem = false;
12490         if (csel.length) {
12491             var ix = this.view.indexOf(csel[0]);
12492             cselitem  = this.store.getAt(ix);
12493             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12494                 cselitem = false;
12495             }
12496             
12497         }
12498         
12499         this.store.each(function(v) { 
12500             if (cselitem) {
12501                 // start at existing selection.
12502                 if (cselitem.id == v.id) {
12503                     cselitem = false;
12504                 }
12505                 return true;
12506             }
12507                 
12508             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12509                 match = this.store.indexOf(v);
12510                 return false;
12511             }
12512             return true;
12513         }, this);
12514         
12515         if (match === false) {
12516             return true; // no more action?
12517         }
12518         // scroll to?
12519         this.view.select(match);
12520         var sn = Roo.get(this.view.getSelectedNodes()[0])
12521         sn.scrollIntoView(sn.dom.parentNode, false);
12522     },
12523     
12524     onViewScroll : function(e, t){
12525         
12526         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){
12527             return;
12528         }
12529         
12530         this.hasQuery = true;
12531         
12532         this.loading = this.list.select('.loading', true).first();
12533         
12534         if(this.loading === null){
12535             this.list.createChild({
12536                 tag: 'div',
12537                 cls: 'loading select2-more-results select2-active',
12538                 html: 'Loading more results...'
12539             })
12540             
12541             this.loading = this.list.select('.loading', true).first();
12542             
12543             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12544             
12545             this.loading.hide();
12546         }
12547         
12548         this.loading.show();
12549         
12550         var _combo = this;
12551         
12552         this.page++;
12553         this.loadNext = true;
12554         
12555         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12556         
12557         return;
12558     },
12559     
12560     addItem : function(o)
12561     {   
12562         var dv = ''; // display value
12563         
12564         if (this.displayField) {
12565             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12566         } else {
12567             // this is an error condition!!!
12568             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12569         }
12570         
12571         if(!dv.length){
12572             return;
12573         }
12574         
12575         var choice = this.choices.createChild({
12576             tag: 'li',
12577             cls: 'select2-search-choice',
12578             cn: [
12579                 {
12580                     tag: 'div',
12581                     html: dv
12582                 },
12583                 {
12584                     tag: 'a',
12585                     href: '#',
12586                     cls: 'select2-search-choice-close',
12587                     tabindex: '-1'
12588                 }
12589             ]
12590             
12591         }, this.searchField);
12592         
12593         var close = choice.select('a.select2-search-choice-close', true).first()
12594         
12595         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12596         
12597         this.item.push(o);
12598         
12599         this.lastData = o;
12600         
12601         this.syncValue();
12602         
12603         this.inputEl().dom.value = '';
12604         
12605         this.validate();
12606     },
12607     
12608     onRemoveItem : function(e, _self, o)
12609     {
12610         e.preventDefault();
12611         
12612         this.lastItem = Roo.apply([], this.item);
12613         
12614         var index = this.item.indexOf(o.data) * 1;
12615         
12616         if( index < 0){
12617             Roo.log('not this item?!');
12618             return;
12619         }
12620         
12621         this.item.splice(index, 1);
12622         o.item.remove();
12623         
12624         this.syncValue();
12625         
12626         this.fireEvent('remove', this, e);
12627         
12628         this.validate();
12629         
12630     },
12631     
12632     syncValue : function()
12633     {
12634         if(!this.item.length){
12635             this.clearValue();
12636             return;
12637         }
12638             
12639         var value = [];
12640         var _this = this;
12641         Roo.each(this.item, function(i){
12642             if(_this.valueField){
12643                 value.push(i[_this.valueField]);
12644                 return;
12645             }
12646
12647             value.push(i);
12648         });
12649
12650         this.value = value.join(',');
12651
12652         if(this.hiddenField){
12653             this.hiddenField.dom.value = this.value;
12654         }
12655         
12656         this.store.fireEvent("datachanged", this.store);
12657     },
12658     
12659     clearItem : function()
12660     {
12661         if(!this.multiple){
12662             return;
12663         }
12664         
12665         this.item = [];
12666         
12667         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12668            c.remove();
12669         });
12670         
12671         this.syncValue();
12672         
12673         this.validate();
12674     },
12675     
12676     inputEl: function ()
12677     {
12678         if(this.tickable){
12679             return this.searchField;
12680         }
12681         return this.el.select('input.form-control',true).first();
12682     },
12683     
12684     
12685     onTickableFooterButtonClick : function(e, btn, el)
12686     {
12687         e.preventDefault();
12688         
12689         this.lastItem = Roo.apply([], this.item);
12690         
12691         if(btn && btn.name == 'cancel'){
12692             this.tickItems = Roo.apply([], this.item);
12693             this.collapse();
12694             return;
12695         }
12696         
12697         this.clearItem();
12698         
12699         var _this = this;
12700         
12701         Roo.each(this.tickItems, function(o){
12702             _this.addItem(o);
12703         });
12704         
12705         this.collapse();
12706         
12707     },
12708     
12709     validate : function()
12710     {
12711         var v = this.getRawValue();
12712         
12713         if(this.multiple){
12714             v = this.getValue();
12715         }
12716         
12717         if(this.disabled || this.allowBlank || v.length){
12718             this.markValid();
12719             return true;
12720         }
12721         
12722         this.markInvalid();
12723         return false;
12724     },
12725     
12726     tickableInputEl : function()
12727     {
12728         if(!this.tickable || !this.editable){
12729             return this.inputEl();
12730         }
12731         
12732         return this.inputEl().select('.select2-search-field-input', true).first();
12733     }
12734     
12735     
12736
12737     /** 
12738     * @cfg {Boolean} grow 
12739     * @hide 
12740     */
12741     /** 
12742     * @cfg {Number} growMin 
12743     * @hide 
12744     */
12745     /** 
12746     * @cfg {Number} growMax 
12747     * @hide 
12748     */
12749     /**
12750      * @hide
12751      * @method autoSize
12752      */
12753 });
12754 /*
12755  * Based on:
12756  * Ext JS Library 1.1.1
12757  * Copyright(c) 2006-2007, Ext JS, LLC.
12758  *
12759  * Originally Released Under LGPL - original licence link has changed is not relivant.
12760  *
12761  * Fork - LGPL
12762  * <script type="text/javascript">
12763  */
12764
12765 /**
12766  * @class Roo.View
12767  * @extends Roo.util.Observable
12768  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12769  * This class also supports single and multi selection modes. <br>
12770  * Create a data model bound view:
12771  <pre><code>
12772  var store = new Roo.data.Store(...);
12773
12774  var view = new Roo.View({
12775     el : "my-element",
12776     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12777  
12778     singleSelect: true,
12779     selectedClass: "ydataview-selected",
12780     store: store
12781  });
12782
12783  // listen for node click?
12784  view.on("click", function(vw, index, node, e){
12785  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12786  });
12787
12788  // load XML data
12789  dataModel.load("foobar.xml");
12790  </code></pre>
12791  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12792  * <br><br>
12793  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12794  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12795  * 
12796  * Note: old style constructor is still suported (container, template, config)
12797  * 
12798  * @constructor
12799  * Create a new View
12800  * @param {Object} config The config object
12801  * 
12802  */
12803 Roo.View = function(config, depreciated_tpl, depreciated_config){
12804     
12805     this.parent = false;
12806     
12807     if (typeof(depreciated_tpl) == 'undefined') {
12808         // new way.. - universal constructor.
12809         Roo.apply(this, config);
12810         this.el  = Roo.get(this.el);
12811     } else {
12812         // old format..
12813         this.el  = Roo.get(config);
12814         this.tpl = depreciated_tpl;
12815         Roo.apply(this, depreciated_config);
12816     }
12817     this.wrapEl  = this.el.wrap().wrap();
12818     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12819     
12820     
12821     if(typeof(this.tpl) == "string"){
12822         this.tpl = new Roo.Template(this.tpl);
12823     } else {
12824         // support xtype ctors..
12825         this.tpl = new Roo.factory(this.tpl, Roo);
12826     }
12827     
12828     
12829     this.tpl.compile();
12830     
12831     /** @private */
12832     this.addEvents({
12833         /**
12834          * @event beforeclick
12835          * Fires before a click is processed. Returns false to cancel the default action.
12836          * @param {Roo.View} this
12837          * @param {Number} index The index of the target node
12838          * @param {HTMLElement} node The target node
12839          * @param {Roo.EventObject} e The raw event object
12840          */
12841             "beforeclick" : true,
12842         /**
12843          * @event click
12844          * Fires when a template node is clicked.
12845          * @param {Roo.View} this
12846          * @param {Number} index The index of the target node
12847          * @param {HTMLElement} node The target node
12848          * @param {Roo.EventObject} e The raw event object
12849          */
12850             "click" : true,
12851         /**
12852          * @event dblclick
12853          * Fires when a template node is double clicked.
12854          * @param {Roo.View} this
12855          * @param {Number} index The index of the target node
12856          * @param {HTMLElement} node The target node
12857          * @param {Roo.EventObject} e The raw event object
12858          */
12859             "dblclick" : true,
12860         /**
12861          * @event contextmenu
12862          * Fires when a template node is right clicked.
12863          * @param {Roo.View} this
12864          * @param {Number} index The index of the target node
12865          * @param {HTMLElement} node The target node
12866          * @param {Roo.EventObject} e The raw event object
12867          */
12868             "contextmenu" : true,
12869         /**
12870          * @event selectionchange
12871          * Fires when the selected nodes change.
12872          * @param {Roo.View} this
12873          * @param {Array} selections Array of the selected nodes
12874          */
12875             "selectionchange" : true,
12876     
12877         /**
12878          * @event beforeselect
12879          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12880          * @param {Roo.View} this
12881          * @param {HTMLElement} node The node to be selected
12882          * @param {Array} selections Array of currently selected nodes
12883          */
12884             "beforeselect" : true,
12885         /**
12886          * @event preparedata
12887          * Fires on every row to render, to allow you to change the data.
12888          * @param {Roo.View} this
12889          * @param {Object} data to be rendered (change this)
12890          */
12891           "preparedata" : true
12892           
12893           
12894         });
12895
12896
12897
12898     this.el.on({
12899         "click": this.onClick,
12900         "dblclick": this.onDblClick,
12901         "contextmenu": this.onContextMenu,
12902         scope:this
12903     });
12904
12905     this.selections = [];
12906     this.nodes = [];
12907     this.cmp = new Roo.CompositeElementLite([]);
12908     if(this.store){
12909         this.store = Roo.factory(this.store, Roo.data);
12910         this.setStore(this.store, true);
12911     }
12912     
12913     if ( this.footer && this.footer.xtype) {
12914            
12915          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12916         
12917         this.footer.dataSource = this.store
12918         this.footer.container = fctr;
12919         this.footer = Roo.factory(this.footer, Roo);
12920         fctr.insertFirst(this.el);
12921         
12922         // this is a bit insane - as the paging toolbar seems to detach the el..
12923 //        dom.parentNode.parentNode.parentNode
12924          // they get detached?
12925     }
12926     
12927     
12928     Roo.View.superclass.constructor.call(this);
12929     
12930     
12931 };
12932
12933 Roo.extend(Roo.View, Roo.util.Observable, {
12934     
12935      /**
12936      * @cfg {Roo.data.Store} store Data store to load data from.
12937      */
12938     store : false,
12939     
12940     /**
12941      * @cfg {String|Roo.Element} el The container element.
12942      */
12943     el : '',
12944     
12945     /**
12946      * @cfg {String|Roo.Template} tpl The template used by this View 
12947      */
12948     tpl : false,
12949     /**
12950      * @cfg {String} dataName the named area of the template to use as the data area
12951      *                          Works with domtemplates roo-name="name"
12952      */
12953     dataName: false,
12954     /**
12955      * @cfg {String} selectedClass The css class to add to selected nodes
12956      */
12957     selectedClass : "x-view-selected",
12958      /**
12959      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12960      */
12961     emptyText : "",
12962     
12963     /**
12964      * @cfg {String} text to display on mask (default Loading)
12965      */
12966     mask : false,
12967     /**
12968      * @cfg {Boolean} multiSelect Allow multiple selection
12969      */
12970     multiSelect : false,
12971     /**
12972      * @cfg {Boolean} singleSelect Allow single selection
12973      */
12974     singleSelect:  false,
12975     
12976     /**
12977      * @cfg {Boolean} toggleSelect - selecting 
12978      */
12979     toggleSelect : false,
12980     
12981     /**
12982      * @cfg {Boolean} tickable - selecting 
12983      */
12984     tickable : false,
12985     
12986     /**
12987      * Returns the element this view is bound to.
12988      * @return {Roo.Element}
12989      */
12990     getEl : function(){
12991         return this.wrapEl;
12992     },
12993     
12994     
12995
12996     /**
12997      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12998      */
12999     refresh : function(){
13000         //Roo.log('refresh');
13001         var t = this.tpl;
13002         
13003         // if we are using something like 'domtemplate', then
13004         // the what gets used is:
13005         // t.applySubtemplate(NAME, data, wrapping data..)
13006         // the outer template then get' applied with
13007         //     the store 'extra data'
13008         // and the body get's added to the
13009         //      roo-name="data" node?
13010         //      <span class='roo-tpl-{name}'></span> ?????
13011         
13012         
13013         
13014         this.clearSelections();
13015         this.el.update("");
13016         var html = [];
13017         var records = this.store.getRange();
13018         if(records.length < 1) {
13019             
13020             // is this valid??  = should it render a template??
13021             
13022             this.el.update(this.emptyText);
13023             return;
13024         }
13025         var el = this.el;
13026         if (this.dataName) {
13027             this.el.update(t.apply(this.store.meta)); //????
13028             el = this.el.child('.roo-tpl-' + this.dataName);
13029         }
13030         
13031         for(var i = 0, len = records.length; i < len; i++){
13032             var data = this.prepareData(records[i].data, i, records[i]);
13033             this.fireEvent("preparedata", this, data, i, records[i]);
13034             
13035             var d = Roo.apply({}, data);
13036             
13037             if(this.tickable){
13038                 Roo.apply(d, {'roo-id' : Roo.id()});
13039                 
13040                 var _this = this;
13041             
13042                 Roo.each(this.parent.item, function(item){
13043                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13044                         return;
13045                     }
13046                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13047                 });
13048             }
13049             
13050             html[html.length] = Roo.util.Format.trim(
13051                 this.dataName ?
13052                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13053                     t.apply(d)
13054             );
13055         }
13056         
13057         
13058         
13059         el.update(html.join(""));
13060         this.nodes = el.dom.childNodes;
13061         this.updateIndexes(0);
13062     },
13063     
13064
13065     /**
13066      * Function to override to reformat the data that is sent to
13067      * the template for each node.
13068      * DEPRICATED - use the preparedata event handler.
13069      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13070      * a JSON object for an UpdateManager bound view).
13071      */
13072     prepareData : function(data, index, record)
13073     {
13074         this.fireEvent("preparedata", this, data, index, record);
13075         return data;
13076     },
13077
13078     onUpdate : function(ds, record){
13079         // Roo.log('on update');   
13080         this.clearSelections();
13081         var index = this.store.indexOf(record);
13082         var n = this.nodes[index];
13083         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13084         n.parentNode.removeChild(n);
13085         this.updateIndexes(index, index);
13086     },
13087
13088     
13089     
13090 // --------- FIXME     
13091     onAdd : function(ds, records, index)
13092     {
13093         //Roo.log(['on Add', ds, records, index] );        
13094         this.clearSelections();
13095         if(this.nodes.length == 0){
13096             this.refresh();
13097             return;
13098         }
13099         var n = this.nodes[index];
13100         for(var i = 0, len = records.length; i < len; i++){
13101             var d = this.prepareData(records[i].data, i, records[i]);
13102             if(n){
13103                 this.tpl.insertBefore(n, d);
13104             }else{
13105                 
13106                 this.tpl.append(this.el, d);
13107             }
13108         }
13109         this.updateIndexes(index);
13110     },
13111
13112     onRemove : function(ds, record, index){
13113        // Roo.log('onRemove');
13114         this.clearSelections();
13115         var el = this.dataName  ?
13116             this.el.child('.roo-tpl-' + this.dataName) :
13117             this.el; 
13118         
13119         el.dom.removeChild(this.nodes[index]);
13120         this.updateIndexes(index);
13121     },
13122
13123     /**
13124      * Refresh an individual node.
13125      * @param {Number} index
13126      */
13127     refreshNode : function(index){
13128         this.onUpdate(this.store, this.store.getAt(index));
13129     },
13130
13131     updateIndexes : function(startIndex, endIndex){
13132         var ns = this.nodes;
13133         startIndex = startIndex || 0;
13134         endIndex = endIndex || ns.length - 1;
13135         for(var i = startIndex; i <= endIndex; i++){
13136             ns[i].nodeIndex = i;
13137         }
13138     },
13139
13140     /**
13141      * Changes the data store this view uses and refresh the view.
13142      * @param {Store} store
13143      */
13144     setStore : function(store, initial){
13145         if(!initial && this.store){
13146             this.store.un("datachanged", this.refresh);
13147             this.store.un("add", this.onAdd);
13148             this.store.un("remove", this.onRemove);
13149             this.store.un("update", this.onUpdate);
13150             this.store.un("clear", this.refresh);
13151             this.store.un("beforeload", this.onBeforeLoad);
13152             this.store.un("load", this.onLoad);
13153             this.store.un("loadexception", this.onLoad);
13154         }
13155         if(store){
13156           
13157             store.on("datachanged", this.refresh, this);
13158             store.on("add", this.onAdd, this);
13159             store.on("remove", this.onRemove, this);
13160             store.on("update", this.onUpdate, this);
13161             store.on("clear", this.refresh, this);
13162             store.on("beforeload", this.onBeforeLoad, this);
13163             store.on("load", this.onLoad, this);
13164             store.on("loadexception", this.onLoad, this);
13165         }
13166         
13167         if(store){
13168             this.refresh();
13169         }
13170     },
13171     /**
13172      * onbeforeLoad - masks the loading area.
13173      *
13174      */
13175     onBeforeLoad : function(store,opts)
13176     {
13177          //Roo.log('onBeforeLoad');   
13178         if (!opts.add) {
13179             this.el.update("");
13180         }
13181         this.el.mask(this.mask ? this.mask : "Loading" ); 
13182     },
13183     onLoad : function ()
13184     {
13185         this.el.unmask();
13186     },
13187     
13188
13189     /**
13190      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13191      * @param {HTMLElement} node
13192      * @return {HTMLElement} The template node
13193      */
13194     findItemFromChild : function(node){
13195         var el = this.dataName  ?
13196             this.el.child('.roo-tpl-' + this.dataName,true) :
13197             this.el.dom; 
13198         
13199         if(!node || node.parentNode == el){
13200                     return node;
13201             }
13202             var p = node.parentNode;
13203             while(p && p != el){
13204             if(p.parentNode == el){
13205                 return p;
13206             }
13207             p = p.parentNode;
13208         }
13209             return null;
13210     },
13211
13212     /** @ignore */
13213     onClick : function(e){
13214         var item = this.findItemFromChild(e.getTarget());
13215         if(item){
13216             var index = this.indexOf(item);
13217             if(this.onItemClick(item, index, e) !== false){
13218                 this.fireEvent("click", this, index, item, e);
13219             }
13220         }else{
13221             this.clearSelections();
13222         }
13223     },
13224
13225     /** @ignore */
13226     onContextMenu : function(e){
13227         var item = this.findItemFromChild(e.getTarget());
13228         if(item){
13229             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13230         }
13231     },
13232
13233     /** @ignore */
13234     onDblClick : function(e){
13235         var item = this.findItemFromChild(e.getTarget());
13236         if(item){
13237             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13238         }
13239     },
13240
13241     onItemClick : function(item, index, e)
13242     {
13243         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13244             return false;
13245         }
13246         if (this.toggleSelect) {
13247             var m = this.isSelected(item) ? 'unselect' : 'select';
13248             //Roo.log(m);
13249             var _t = this;
13250             _t[m](item, true, false);
13251             return true;
13252         }
13253         if(this.multiSelect || this.singleSelect){
13254             if(this.multiSelect && e.shiftKey && this.lastSelection){
13255                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13256             }else{
13257                 this.select(item, this.multiSelect && e.ctrlKey);
13258                 this.lastSelection = item;
13259             }
13260             
13261             if(!this.tickable){
13262                 e.preventDefault();
13263             }
13264             
13265         }
13266         return true;
13267     },
13268
13269     /**
13270      * Get the number of selected nodes.
13271      * @return {Number}
13272      */
13273     getSelectionCount : function(){
13274         return this.selections.length;
13275     },
13276
13277     /**
13278      * Get the currently selected nodes.
13279      * @return {Array} An array of HTMLElements
13280      */
13281     getSelectedNodes : function(){
13282         return this.selections;
13283     },
13284
13285     /**
13286      * Get the indexes of the selected nodes.
13287      * @return {Array}
13288      */
13289     getSelectedIndexes : function(){
13290         var indexes = [], s = this.selections;
13291         for(var i = 0, len = s.length; i < len; i++){
13292             indexes.push(s[i].nodeIndex);
13293         }
13294         return indexes;
13295     },
13296
13297     /**
13298      * Clear all selections
13299      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13300      */
13301     clearSelections : function(suppressEvent){
13302         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13303             this.cmp.elements = this.selections;
13304             this.cmp.removeClass(this.selectedClass);
13305             this.selections = [];
13306             if(!suppressEvent){
13307                 this.fireEvent("selectionchange", this, this.selections);
13308             }
13309         }
13310     },
13311
13312     /**
13313      * Returns true if the passed node is selected
13314      * @param {HTMLElement/Number} node The node or node index
13315      * @return {Boolean}
13316      */
13317     isSelected : function(node){
13318         var s = this.selections;
13319         if(s.length < 1){
13320             return false;
13321         }
13322         node = this.getNode(node);
13323         return s.indexOf(node) !== -1;
13324     },
13325
13326     /**
13327      * Selects nodes.
13328      * @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
13329      * @param {Boolean} keepExisting (optional) true to keep existing selections
13330      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13331      */
13332     select : function(nodeInfo, keepExisting, suppressEvent){
13333         if(nodeInfo instanceof Array){
13334             if(!keepExisting){
13335                 this.clearSelections(true);
13336             }
13337             for(var i = 0, len = nodeInfo.length; i < len; i++){
13338                 this.select(nodeInfo[i], true, true);
13339             }
13340             return;
13341         } 
13342         var node = this.getNode(nodeInfo);
13343         if(!node || this.isSelected(node)){
13344             return; // already selected.
13345         }
13346         if(!keepExisting){
13347             this.clearSelections(true);
13348         }
13349         
13350         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13351             Roo.fly(node).addClass(this.selectedClass);
13352             this.selections.push(node);
13353             if(!suppressEvent){
13354                 this.fireEvent("selectionchange", this, this.selections);
13355             }
13356         }
13357         
13358         
13359     },
13360       /**
13361      * Unselects nodes.
13362      * @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
13363      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13364      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13365      */
13366     unselect : function(nodeInfo, keepExisting, suppressEvent)
13367     {
13368         if(nodeInfo instanceof Array){
13369             Roo.each(this.selections, function(s) {
13370                 this.unselect(s, nodeInfo);
13371             }, this);
13372             return;
13373         }
13374         var node = this.getNode(nodeInfo);
13375         if(!node || !this.isSelected(node)){
13376             //Roo.log("not selected");
13377             return; // not selected.
13378         }
13379         // fireevent???
13380         var ns = [];
13381         Roo.each(this.selections, function(s) {
13382             if (s == node ) {
13383                 Roo.fly(node).removeClass(this.selectedClass);
13384
13385                 return;
13386             }
13387             ns.push(s);
13388         },this);
13389         
13390         this.selections= ns;
13391         this.fireEvent("selectionchange", this, this.selections);
13392     },
13393
13394     /**
13395      * Gets a template node.
13396      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13397      * @return {HTMLElement} The node or null if it wasn't found
13398      */
13399     getNode : function(nodeInfo){
13400         if(typeof nodeInfo == "string"){
13401             return document.getElementById(nodeInfo);
13402         }else if(typeof nodeInfo == "number"){
13403             return this.nodes[nodeInfo];
13404         }
13405         return nodeInfo;
13406     },
13407
13408     /**
13409      * Gets a range template nodes.
13410      * @param {Number} startIndex
13411      * @param {Number} endIndex
13412      * @return {Array} An array of nodes
13413      */
13414     getNodes : function(start, end){
13415         var ns = this.nodes;
13416         start = start || 0;
13417         end = typeof end == "undefined" ? ns.length - 1 : end;
13418         var nodes = [];
13419         if(start <= end){
13420             for(var i = start; i <= end; i++){
13421                 nodes.push(ns[i]);
13422             }
13423         } else{
13424             for(var i = start; i >= end; i--){
13425                 nodes.push(ns[i]);
13426             }
13427         }
13428         return nodes;
13429     },
13430
13431     /**
13432      * Finds the index of the passed node
13433      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13434      * @return {Number} The index of the node or -1
13435      */
13436     indexOf : function(node){
13437         node = this.getNode(node);
13438         if(typeof node.nodeIndex == "number"){
13439             return node.nodeIndex;
13440         }
13441         var ns = this.nodes;
13442         for(var i = 0, len = ns.length; i < len; i++){
13443             if(ns[i] == node){
13444                 return i;
13445             }
13446         }
13447         return -1;
13448     }
13449 });
13450 /*
13451  * - LGPL
13452  *
13453  * based on jquery fullcalendar
13454  * 
13455  */
13456
13457 Roo.bootstrap = Roo.bootstrap || {};
13458 /**
13459  * @class Roo.bootstrap.Calendar
13460  * @extends Roo.bootstrap.Component
13461  * Bootstrap Calendar class
13462  * @cfg {Boolean} loadMask (true|false) default false
13463  * @cfg {Object} header generate the user specific header of the calendar, default false
13464
13465  * @constructor
13466  * Create a new Container
13467  * @param {Object} config The config object
13468  */
13469
13470
13471
13472 Roo.bootstrap.Calendar = function(config){
13473     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13474      this.addEvents({
13475         /**
13476              * @event select
13477              * Fires when a date is selected
13478              * @param {DatePicker} this
13479              * @param {Date} date The selected date
13480              */
13481         'select': true,
13482         /**
13483              * @event monthchange
13484              * Fires when the displayed month changes 
13485              * @param {DatePicker} this
13486              * @param {Date} date The selected month
13487              */
13488         'monthchange': true,
13489         /**
13490              * @event evententer
13491              * Fires when mouse over an event
13492              * @param {Calendar} this
13493              * @param {event} Event
13494              */
13495         'evententer': true,
13496         /**
13497              * @event eventleave
13498              * Fires when the mouse leaves an
13499              * @param {Calendar} this
13500              * @param {event}
13501              */
13502         'eventleave': true,
13503         /**
13504              * @event eventclick
13505              * Fires when the mouse click an
13506              * @param {Calendar} this
13507              * @param {event}
13508              */
13509         'eventclick': true
13510         
13511     });
13512
13513 };
13514
13515 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13516     
13517      /**
13518      * @cfg {Number} startDay
13519      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13520      */
13521     startDay : 0,
13522     
13523     loadMask : false,
13524     
13525     header : false,
13526       
13527     getAutoCreate : function(){
13528         
13529         
13530         var fc_button = function(name, corner, style, content ) {
13531             return Roo.apply({},{
13532                 tag : 'span',
13533                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13534                          (corner.length ?
13535                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13536                             ''
13537                         ),
13538                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13539                 unselectable: 'on'
13540             });
13541         };
13542         
13543         var header = {};
13544         
13545         if(!this.header){
13546             header = {
13547                 tag : 'table',
13548                 cls : 'fc-header',
13549                 style : 'width:100%',
13550                 cn : [
13551                     {
13552                         tag: 'tr',
13553                         cn : [
13554                             {
13555                                 tag : 'td',
13556                                 cls : 'fc-header-left',
13557                                 cn : [
13558                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13559                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13560                                     { tag: 'span', cls: 'fc-header-space' },
13561                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13562
13563
13564                                 ]
13565                             },
13566
13567                             {
13568                                 tag : 'td',
13569                                 cls : 'fc-header-center',
13570                                 cn : [
13571                                     {
13572                                         tag: 'span',
13573                                         cls: 'fc-header-title',
13574                                         cn : {
13575                                             tag: 'H2',
13576                                             html : 'month / year'
13577                                         }
13578                                     }
13579
13580                                 ]
13581                             },
13582                             {
13583                                 tag : 'td',
13584                                 cls : 'fc-header-right',
13585                                 cn : [
13586                               /*      fc_button('month', 'left', '', 'month' ),
13587                                     fc_button('week', '', '', 'week' ),
13588                                     fc_button('day', 'right', '', 'day' )
13589                                 */    
13590
13591                                 ]
13592                             }
13593
13594                         ]
13595                     }
13596                 ]
13597             };
13598         }
13599         
13600         header = this.header;
13601         
13602        
13603         var cal_heads = function() {
13604             var ret = [];
13605             // fixme - handle this.
13606             
13607             for (var i =0; i < Date.dayNames.length; i++) {
13608                 var d = Date.dayNames[i];
13609                 ret.push({
13610                     tag: 'th',
13611                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13612                     html : d.substring(0,3)
13613                 });
13614                 
13615             }
13616             ret[0].cls += ' fc-first';
13617             ret[6].cls += ' fc-last';
13618             return ret;
13619         };
13620         var cal_cell = function(n) {
13621             return  {
13622                 tag: 'td',
13623                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13624                 cn : [
13625                     {
13626                         cn : [
13627                             {
13628                                 cls: 'fc-day-number',
13629                                 html: 'D'
13630                             },
13631                             {
13632                                 cls: 'fc-day-content',
13633                              
13634                                 cn : [
13635                                      {
13636                                         style: 'position: relative;' // height: 17px;
13637                                     }
13638                                 ]
13639                             }
13640                             
13641                             
13642                         ]
13643                     }
13644                 ]
13645                 
13646             }
13647         };
13648         var cal_rows = function() {
13649             
13650             var ret = [];
13651             for (var r = 0; r < 6; r++) {
13652                 var row= {
13653                     tag : 'tr',
13654                     cls : 'fc-week',
13655                     cn : []
13656                 };
13657                 
13658                 for (var i =0; i < Date.dayNames.length; i++) {
13659                     var d = Date.dayNames[i];
13660                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13661
13662                 }
13663                 row.cn[0].cls+=' fc-first';
13664                 row.cn[0].cn[0].style = 'min-height:90px';
13665                 row.cn[6].cls+=' fc-last';
13666                 ret.push(row);
13667                 
13668             }
13669             ret[0].cls += ' fc-first';
13670             ret[4].cls += ' fc-prev-last';
13671             ret[5].cls += ' fc-last';
13672             return ret;
13673             
13674         };
13675         
13676         var cal_table = {
13677             tag: 'table',
13678             cls: 'fc-border-separate',
13679             style : 'width:100%',
13680             cellspacing  : 0,
13681             cn : [
13682                 { 
13683                     tag: 'thead',
13684                     cn : [
13685                         { 
13686                             tag: 'tr',
13687                             cls : 'fc-first fc-last',
13688                             cn : cal_heads()
13689                         }
13690                     ]
13691                 },
13692                 { 
13693                     tag: 'tbody',
13694                     cn : cal_rows()
13695                 }
13696                   
13697             ]
13698         };
13699          
13700          var cfg = {
13701             cls : 'fc fc-ltr',
13702             cn : [
13703                 header,
13704                 {
13705                     cls : 'fc-content',
13706                     style : "position: relative;",
13707                     cn : [
13708                         {
13709                             cls : 'fc-view fc-view-month fc-grid',
13710                             style : 'position: relative',
13711                             unselectable : 'on',
13712                             cn : [
13713                                 {
13714                                     cls : 'fc-event-container',
13715                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13716                                 },
13717                                 cal_table
13718                             ]
13719                         }
13720                     ]
13721     
13722                 }
13723            ] 
13724             
13725         };
13726         
13727          
13728         
13729         return cfg;
13730     },
13731     
13732     
13733     initEvents : function()
13734     {
13735         if(!this.store){
13736             throw "can not find store for calendar";
13737         }
13738         
13739         var mark = {
13740             tag: "div",
13741             cls:"x-dlg-mask",
13742             style: "text-align:center",
13743             cn: [
13744                 {
13745                     tag: "div",
13746                     style: "background-color:white;width:50%;margin:250 auto",
13747                     cn: [
13748                         {
13749                             tag: "img",
13750                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13751                         },
13752                         {
13753                             tag: "span",
13754                             html: "Loading"
13755                         }
13756                         
13757                     ]
13758                 }
13759             ]
13760         }
13761         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13762         
13763         var size = this.el.select('.fc-content', true).first().getSize();
13764         this.maskEl.setSize(size.width, size.height);
13765         this.maskEl.enableDisplayMode("block");
13766         if(!this.loadMask){
13767             this.maskEl.hide();
13768         }
13769         
13770         this.store = Roo.factory(this.store, Roo.data);
13771         this.store.on('load', this.onLoad, this);
13772         this.store.on('beforeload', this.onBeforeLoad, this);
13773         
13774         this.resize();
13775         
13776         this.cells = this.el.select('.fc-day',true);
13777         //Roo.log(this.cells);
13778         this.textNodes = this.el.query('.fc-day-number');
13779         this.cells.addClassOnOver('fc-state-hover');
13780         
13781         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13782         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13783         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13784         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13785         
13786         this.on('monthchange', this.onMonthChange, this);
13787         
13788         this.update(new Date().clearTime());
13789     },
13790     
13791     resize : function() {
13792         var sz  = this.el.getSize();
13793         
13794         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13795         this.el.select('.fc-day-content div',true).setHeight(34);
13796     },
13797     
13798     
13799     // private
13800     showPrevMonth : function(e){
13801         this.update(this.activeDate.add("mo", -1));
13802     },
13803     showToday : function(e){
13804         this.update(new Date().clearTime());
13805     },
13806     // private
13807     showNextMonth : function(e){
13808         this.update(this.activeDate.add("mo", 1));
13809     },
13810
13811     // private
13812     showPrevYear : function(){
13813         this.update(this.activeDate.add("y", -1));
13814     },
13815
13816     // private
13817     showNextYear : function(){
13818         this.update(this.activeDate.add("y", 1));
13819     },
13820
13821     
13822    // private
13823     update : function(date)
13824     {
13825         var vd = this.activeDate;
13826         this.activeDate = date;
13827 //        if(vd && this.el){
13828 //            var t = date.getTime();
13829 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13830 //                Roo.log('using add remove');
13831 //                
13832 //                this.fireEvent('monthchange', this, date);
13833 //                
13834 //                this.cells.removeClass("fc-state-highlight");
13835 //                this.cells.each(function(c){
13836 //                   if(c.dateValue == t){
13837 //                       c.addClass("fc-state-highlight");
13838 //                       setTimeout(function(){
13839 //                            try{c.dom.firstChild.focus();}catch(e){}
13840 //                       }, 50);
13841 //                       return false;
13842 //                   }
13843 //                   return true;
13844 //                });
13845 //                return;
13846 //            }
13847 //        }
13848         
13849         var days = date.getDaysInMonth();
13850         
13851         var firstOfMonth = date.getFirstDateOfMonth();
13852         var startingPos = firstOfMonth.getDay()-this.startDay;
13853         
13854         if(startingPos < this.startDay){
13855             startingPos += 7;
13856         }
13857         
13858         var pm = date.add(Date.MONTH, -1);
13859         var prevStart = pm.getDaysInMonth()-startingPos;
13860 //        
13861         this.cells = this.el.select('.fc-day',true);
13862         this.textNodes = this.el.query('.fc-day-number');
13863         this.cells.addClassOnOver('fc-state-hover');
13864         
13865         var cells = this.cells.elements;
13866         var textEls = this.textNodes;
13867         
13868         Roo.each(cells, function(cell){
13869             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13870         });
13871         
13872         days += startingPos;
13873
13874         // convert everything to numbers so it's fast
13875         var day = 86400000;
13876         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13877         //Roo.log(d);
13878         //Roo.log(pm);
13879         //Roo.log(prevStart);
13880         
13881         var today = new Date().clearTime().getTime();
13882         var sel = date.clearTime().getTime();
13883         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13884         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13885         var ddMatch = this.disabledDatesRE;
13886         var ddText = this.disabledDatesText;
13887         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13888         var ddaysText = this.disabledDaysText;
13889         var format = this.format;
13890         
13891         var setCellClass = function(cal, cell){
13892             cell.row = 0;
13893             cell.events = [];
13894             cell.more = [];
13895             //Roo.log('set Cell Class');
13896             cell.title = "";
13897             var t = d.getTime();
13898             
13899             //Roo.log(d);
13900             
13901             cell.dateValue = t;
13902             if(t == today){
13903                 cell.className += " fc-today";
13904                 cell.className += " fc-state-highlight";
13905                 cell.title = cal.todayText;
13906             }
13907             if(t == sel){
13908                 // disable highlight in other month..
13909                 //cell.className += " fc-state-highlight";
13910                 
13911             }
13912             // disabling
13913             if(t < min) {
13914                 cell.className = " fc-state-disabled";
13915                 cell.title = cal.minText;
13916                 return;
13917             }
13918             if(t > max) {
13919                 cell.className = " fc-state-disabled";
13920                 cell.title = cal.maxText;
13921                 return;
13922             }
13923             if(ddays){
13924                 if(ddays.indexOf(d.getDay()) != -1){
13925                     cell.title = ddaysText;
13926                     cell.className = " fc-state-disabled";
13927                 }
13928             }
13929             if(ddMatch && format){
13930                 var fvalue = d.dateFormat(format);
13931                 if(ddMatch.test(fvalue)){
13932                     cell.title = ddText.replace("%0", fvalue);
13933                     cell.className = " fc-state-disabled";
13934                 }
13935             }
13936             
13937             if (!cell.initialClassName) {
13938                 cell.initialClassName = cell.dom.className;
13939             }
13940             
13941             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13942         };
13943
13944         var i = 0;
13945         
13946         for(; i < startingPos; i++) {
13947             textEls[i].innerHTML = (++prevStart);
13948             d.setDate(d.getDate()+1);
13949             
13950             cells[i].className = "fc-past fc-other-month";
13951             setCellClass(this, cells[i]);
13952         }
13953         
13954         var intDay = 0;
13955         
13956         for(; i < days; i++){
13957             intDay = i - startingPos + 1;
13958             textEls[i].innerHTML = (intDay);
13959             d.setDate(d.getDate()+1);
13960             
13961             cells[i].className = ''; // "x-date-active";
13962             setCellClass(this, cells[i]);
13963         }
13964         var extraDays = 0;
13965         
13966         for(; i < 42; i++) {
13967             textEls[i].innerHTML = (++extraDays);
13968             d.setDate(d.getDate()+1);
13969             
13970             cells[i].className = "fc-future fc-other-month";
13971             setCellClass(this, cells[i]);
13972         }
13973         
13974         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13975         
13976         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13977         
13978         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13979         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13980         
13981         if(totalRows != 6){
13982             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13983             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13984         }
13985         
13986         this.fireEvent('monthchange', this, date);
13987         
13988         
13989         /*
13990         if(!this.internalRender){
13991             var main = this.el.dom.firstChild;
13992             var w = main.offsetWidth;
13993             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13994             Roo.fly(main).setWidth(w);
13995             this.internalRender = true;
13996             // opera does not respect the auto grow header center column
13997             // then, after it gets a width opera refuses to recalculate
13998             // without a second pass
13999             if(Roo.isOpera && !this.secondPass){
14000                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14001                 this.secondPass = true;
14002                 this.update.defer(10, this, [date]);
14003             }
14004         }
14005         */
14006         
14007     },
14008     
14009     findCell : function(dt) {
14010         dt = dt.clearTime().getTime();
14011         var ret = false;
14012         this.cells.each(function(c){
14013             //Roo.log("check " +c.dateValue + '?=' + dt);
14014             if(c.dateValue == dt){
14015                 ret = c;
14016                 return false;
14017             }
14018             return true;
14019         });
14020         
14021         return ret;
14022     },
14023     
14024     findCells : function(ev) {
14025         var s = ev.start.clone().clearTime().getTime();
14026        // Roo.log(s);
14027         var e= ev.end.clone().clearTime().getTime();
14028        // Roo.log(e);
14029         var ret = [];
14030         this.cells.each(function(c){
14031              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14032             
14033             if(c.dateValue > e){
14034                 return ;
14035             }
14036             if(c.dateValue < s){
14037                 return ;
14038             }
14039             ret.push(c);
14040         });
14041         
14042         return ret;    
14043     },
14044     
14045 //    findBestRow: function(cells)
14046 //    {
14047 //        var ret = 0;
14048 //        
14049 //        for (var i =0 ; i < cells.length;i++) {
14050 //            ret  = Math.max(cells[i].rows || 0,ret);
14051 //        }
14052 //        return ret;
14053 //        
14054 //    },
14055     
14056     
14057     addItem : function(ev)
14058     {
14059         // look for vertical location slot in
14060         var cells = this.findCells(ev);
14061         
14062 //        ev.row = this.findBestRow(cells);
14063         
14064         // work out the location.
14065         
14066         var crow = false;
14067         var rows = [];
14068         for(var i =0; i < cells.length; i++) {
14069             
14070             cells[i].row = cells[0].row;
14071             
14072             if(i == 0){
14073                 cells[i].row = cells[i].row + 1;
14074             }
14075             
14076             if (!crow) {
14077                 crow = {
14078                     start : cells[i],
14079                     end :  cells[i]
14080                 };
14081                 continue;
14082             }
14083             if (crow.start.getY() == cells[i].getY()) {
14084                 // on same row.
14085                 crow.end = cells[i];
14086                 continue;
14087             }
14088             // different row.
14089             rows.push(crow);
14090             crow = {
14091                 start: cells[i],
14092                 end : cells[i]
14093             };
14094             
14095         }
14096         
14097         rows.push(crow);
14098         ev.els = [];
14099         ev.rows = rows;
14100         ev.cells = cells;
14101         
14102         cells[0].events.push(ev);
14103         
14104         this.calevents.push(ev);
14105     },
14106     
14107     clearEvents: function() {
14108         
14109         if(!this.calevents){
14110             return;
14111         }
14112         
14113         Roo.each(this.cells.elements, function(c){
14114             c.row = 0;
14115             c.events = [];
14116             c.more = [];
14117         });
14118         
14119         Roo.each(this.calevents, function(e) {
14120             Roo.each(e.els, function(el) {
14121                 el.un('mouseenter' ,this.onEventEnter, this);
14122                 el.un('mouseleave' ,this.onEventLeave, this);
14123                 el.remove();
14124             },this);
14125         },this);
14126         
14127         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14128             e.remove();
14129         });
14130         
14131     },
14132     
14133     renderEvents: function()
14134     {   
14135         var _this = this;
14136         
14137         this.cells.each(function(c) {
14138             
14139             if(c.row < 5){
14140                 return;
14141             }
14142             
14143             var ev = c.events;
14144             
14145             var r = 4;
14146             if(c.row != c.events.length){
14147                 r = 4 - (4 - (c.row - c.events.length));
14148             }
14149             
14150             c.events = ev.slice(0, r);
14151             c.more = ev.slice(r);
14152             
14153             if(c.more.length && c.more.length == 1){
14154                 c.events.push(c.more.pop());
14155             }
14156             
14157             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14158             
14159         });
14160             
14161         this.cells.each(function(c) {
14162             
14163             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14164             
14165             
14166             for (var e = 0; e < c.events.length; e++){
14167                 var ev = c.events[e];
14168                 var rows = ev.rows;
14169                 
14170                 for(var i = 0; i < rows.length; i++) {
14171                 
14172                     // how many rows should it span..
14173
14174                     var  cfg = {
14175                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14176                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14177
14178                         unselectable : "on",
14179                         cn : [
14180                             {
14181                                 cls: 'fc-event-inner',
14182                                 cn : [
14183     //                                {
14184     //                                  tag:'span',
14185     //                                  cls: 'fc-event-time',
14186     //                                  html : cells.length > 1 ? '' : ev.time
14187     //                                },
14188                                     {
14189                                       tag:'span',
14190                                       cls: 'fc-event-title',
14191                                       html : String.format('{0}', ev.title)
14192                                     }
14193
14194
14195                                 ]
14196                             },
14197                             {
14198                                 cls: 'ui-resizable-handle ui-resizable-e',
14199                                 html : '&nbsp;&nbsp;&nbsp'
14200                             }
14201
14202                         ]
14203                     };
14204
14205                     if (i == 0) {
14206                         cfg.cls += ' fc-event-start';
14207                     }
14208                     if ((i+1) == rows.length) {
14209                         cfg.cls += ' fc-event-end';
14210                     }
14211
14212                     var ctr = _this.el.select('.fc-event-container',true).first();
14213                     var cg = ctr.createChild(cfg);
14214
14215                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14216                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14217
14218                     var r = (c.more.length) ? 1 : 0;
14219                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14220                     cg.setWidth(ebox.right - sbox.x -2);
14221
14222                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14223                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14224                     cg.on('click', _this.onEventClick, _this, ev);
14225
14226                     ev.els.push(cg);
14227                     
14228                 }
14229                 
14230             }
14231             
14232             
14233             if(c.more.length){
14234                 var  cfg = {
14235                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14236                     style : 'position: absolute',
14237                     unselectable : "on",
14238                     cn : [
14239                         {
14240                             cls: 'fc-event-inner',
14241                             cn : [
14242                                 {
14243                                   tag:'span',
14244                                   cls: 'fc-event-title',
14245                                   html : 'More'
14246                                 }
14247
14248
14249                             ]
14250                         },
14251                         {
14252                             cls: 'ui-resizable-handle ui-resizable-e',
14253                             html : '&nbsp;&nbsp;&nbsp'
14254                         }
14255
14256                     ]
14257                 };
14258
14259                 var ctr = _this.el.select('.fc-event-container',true).first();
14260                 var cg = ctr.createChild(cfg);
14261
14262                 var sbox = c.select('.fc-day-content',true).first().getBox();
14263                 var ebox = c.select('.fc-day-content',true).first().getBox();
14264                 //Roo.log(cg);
14265                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14266                 cg.setWidth(ebox.right - sbox.x -2);
14267
14268                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14269                 
14270             }
14271             
14272         });
14273         
14274         
14275         
14276     },
14277     
14278     onEventEnter: function (e, el,event,d) {
14279         this.fireEvent('evententer', this, el, event);
14280     },
14281     
14282     onEventLeave: function (e, el,event,d) {
14283         this.fireEvent('eventleave', this, el, event);
14284     },
14285     
14286     onEventClick: function (e, el,event,d) {
14287         this.fireEvent('eventclick', this, el, event);
14288     },
14289     
14290     onMonthChange: function () {
14291         this.store.load();
14292     },
14293     
14294     onMoreEventClick: function(e, el, more)
14295     {
14296         var _this = this;
14297         
14298         this.calpopover.placement = 'right';
14299         this.calpopover.setTitle('More');
14300         
14301         this.calpopover.setContent('');
14302         
14303         var ctr = this.calpopover.el.select('.popover-content', true).first();
14304         
14305         Roo.each(more, function(m){
14306             var cfg = {
14307                 cls : 'fc-event-hori fc-event-draggable',
14308                 html : m.title
14309             }
14310             var cg = ctr.createChild(cfg);
14311             
14312             cg.on('click', _this.onEventClick, _this, m);
14313         });
14314         
14315         this.calpopover.show(el);
14316         
14317         
14318     },
14319     
14320     onLoad: function () 
14321     {   
14322         this.calevents = [];
14323         var cal = this;
14324         
14325         if(this.store.getCount() > 0){
14326             this.store.data.each(function(d){
14327                cal.addItem({
14328                     id : d.data.id,
14329                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14330                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14331                     time : d.data.start_time,
14332                     title : d.data.title,
14333                     description : d.data.description,
14334                     venue : d.data.venue
14335                 });
14336             });
14337         }
14338         
14339         this.renderEvents();
14340         
14341         if(this.calevents.length && this.loadMask){
14342             this.maskEl.hide();
14343         }
14344     },
14345     
14346     onBeforeLoad: function()
14347     {
14348         this.clearEvents();
14349         if(this.loadMask){
14350             this.maskEl.show();
14351         }
14352     }
14353 });
14354
14355  
14356  /*
14357  * - LGPL
14358  *
14359  * element
14360  * 
14361  */
14362
14363 /**
14364  * @class Roo.bootstrap.Popover
14365  * @extends Roo.bootstrap.Component
14366  * Bootstrap Popover class
14367  * @cfg {String} html contents of the popover   (or false to use children..)
14368  * @cfg {String} title of popover (or false to hide)
14369  * @cfg {String} placement how it is placed
14370  * @cfg {String} trigger click || hover (or false to trigger manually)
14371  * @cfg {String} over what (parent or false to trigger manually.)
14372  * @cfg {Number} delay - delay before showing
14373  
14374  * @constructor
14375  * Create a new Popover
14376  * @param {Object} config The config object
14377  */
14378
14379 Roo.bootstrap.Popover = function(config){
14380     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14381 };
14382
14383 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14384     
14385     title: 'Fill in a title',
14386     html: false,
14387     
14388     placement : 'right',
14389     trigger : 'hover', // hover
14390     
14391     delay : 0,
14392     
14393     over: 'parent',
14394     
14395     can_build_overlaid : false,
14396     
14397     getChildContainer : function()
14398     {
14399         return this.el.select('.popover-content',true).first();
14400     },
14401     
14402     getAutoCreate : function(){
14403          Roo.log('make popover?');
14404         var cfg = {
14405            cls : 'popover roo-dynamic',
14406            style: 'display:block',
14407            cn : [
14408                 {
14409                     cls : 'arrow'
14410                 },
14411                 {
14412                     cls : 'popover-inner',
14413                     cn : [
14414                         {
14415                             tag: 'h3',
14416                             cls: 'popover-title',
14417                             html : this.title
14418                         },
14419                         {
14420                             cls : 'popover-content',
14421                             html : this.html
14422                         }
14423                     ]
14424                     
14425                 }
14426            ]
14427         };
14428         
14429         return cfg;
14430     },
14431     setTitle: function(str)
14432     {
14433         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14434     },
14435     setContent: function(str)
14436     {
14437         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14438     },
14439     // as it get's added to the bottom of the page.
14440     onRender : function(ct, position)
14441     {
14442         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14443         if(!this.el){
14444             var cfg = Roo.apply({},  this.getAutoCreate());
14445             cfg.id = Roo.id();
14446             
14447             if (this.cls) {
14448                 cfg.cls += ' ' + this.cls;
14449             }
14450             if (this.style) {
14451                 cfg.style = this.style;
14452             }
14453             Roo.log("adding to ")
14454             this.el = Roo.get(document.body).createChild(cfg, position);
14455             Roo.log(this.el);
14456         }
14457         this.initEvents();
14458     },
14459     
14460     initEvents : function()
14461     {
14462         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14463         this.el.enableDisplayMode('block');
14464         this.el.hide();
14465         if (this.over === false) {
14466             return; 
14467         }
14468         if (this.triggers === false) {
14469             return;
14470         }
14471         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14472         var triggers = this.trigger ? this.trigger.split(' ') : [];
14473         Roo.each(triggers, function(trigger) {
14474         
14475             if (trigger == 'click') {
14476                 on_el.on('click', this.toggle, this);
14477             } else if (trigger != 'manual') {
14478                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14479                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14480       
14481                 on_el.on(eventIn  ,this.enter, this);
14482                 on_el.on(eventOut, this.leave, this);
14483             }
14484         }, this);
14485         
14486     },
14487     
14488     
14489     // private
14490     timeout : null,
14491     hoverState : null,
14492     
14493     toggle : function () {
14494         this.hoverState == 'in' ? this.leave() : this.enter();
14495     },
14496     
14497     enter : function () {
14498        
14499     
14500         clearTimeout(this.timeout);
14501     
14502         this.hoverState = 'in';
14503     
14504         if (!this.delay || !this.delay.show) {
14505             this.show();
14506             return;
14507         }
14508         var _t = this;
14509         this.timeout = setTimeout(function () {
14510             if (_t.hoverState == 'in') {
14511                 _t.show();
14512             }
14513         }, this.delay.show)
14514     },
14515     leave : function() {
14516         clearTimeout(this.timeout);
14517     
14518         this.hoverState = 'out';
14519     
14520         if (!this.delay || !this.delay.hide) {
14521             this.hide();
14522             return;
14523         }
14524         var _t = this;
14525         this.timeout = setTimeout(function () {
14526             if (_t.hoverState == 'out') {
14527                 _t.hide();
14528             }
14529         }, this.delay.hide)
14530     },
14531     
14532     show : function (on_el)
14533     {
14534         if (!on_el) {
14535             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14536         }
14537         // set content.
14538         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14539         if (this.html !== false) {
14540             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14541         }
14542         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14543         if (!this.title.length) {
14544             this.el.select('.popover-title',true).hide();
14545         }
14546         
14547         var placement = typeof this.placement == 'function' ?
14548             this.placement.call(this, this.el, on_el) :
14549             this.placement;
14550             
14551         var autoToken = /\s?auto?\s?/i;
14552         var autoPlace = autoToken.test(placement);
14553         if (autoPlace) {
14554             placement = placement.replace(autoToken, '') || 'top';
14555         }
14556         
14557         //this.el.detach()
14558         //this.el.setXY([0,0]);
14559         this.el.show();
14560         this.el.dom.style.display='block';
14561         this.el.addClass(placement);
14562         
14563         //this.el.appendTo(on_el);
14564         
14565         var p = this.getPosition();
14566         var box = this.el.getBox();
14567         
14568         if (autoPlace) {
14569             // fixme..
14570         }
14571         var align = Roo.bootstrap.Popover.alignment[placement];
14572         this.el.alignTo(on_el, align[0],align[1]);
14573         //var arrow = this.el.select('.arrow',true).first();
14574         //arrow.set(align[2], 
14575         
14576         this.el.addClass('in');
14577         this.hoverState = null;
14578         
14579         if (this.el.hasClass('fade')) {
14580             // fade it?
14581         }
14582         
14583     },
14584     hide : function()
14585     {
14586         this.el.setXY([0,0]);
14587         this.el.removeClass('in');
14588         this.el.hide();
14589         
14590     }
14591     
14592 });
14593
14594 Roo.bootstrap.Popover.alignment = {
14595     'left' : ['r-l', [-10,0], 'right'],
14596     'right' : ['l-r', [10,0], 'left'],
14597     'bottom' : ['t-b', [0,10], 'top'],
14598     'top' : [ 'b-t', [0,-10], 'bottom']
14599 };
14600
14601  /*
14602  * - LGPL
14603  *
14604  * Progress
14605  * 
14606  */
14607
14608 /**
14609  * @class Roo.bootstrap.Progress
14610  * @extends Roo.bootstrap.Component
14611  * Bootstrap Progress class
14612  * @cfg {Boolean} striped striped of the progress bar
14613  * @cfg {Boolean} active animated of the progress bar
14614  * 
14615  * 
14616  * @constructor
14617  * Create a new Progress
14618  * @param {Object} config The config object
14619  */
14620
14621 Roo.bootstrap.Progress = function(config){
14622     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14623 };
14624
14625 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14626     
14627     striped : false,
14628     active: false,
14629     
14630     getAutoCreate : function(){
14631         var cfg = {
14632             tag: 'div',
14633             cls: 'progress'
14634         };
14635         
14636         
14637         if(this.striped){
14638             cfg.cls += ' progress-striped';
14639         }
14640       
14641         if(this.active){
14642             cfg.cls += ' active';
14643         }
14644         
14645         
14646         return cfg;
14647     }
14648    
14649 });
14650
14651  
14652
14653  /*
14654  * - LGPL
14655  *
14656  * ProgressBar
14657  * 
14658  */
14659
14660 /**
14661  * @class Roo.bootstrap.ProgressBar
14662  * @extends Roo.bootstrap.Component
14663  * Bootstrap ProgressBar class
14664  * @cfg {Number} aria_valuenow aria-value now
14665  * @cfg {Number} aria_valuemin aria-value min
14666  * @cfg {Number} aria_valuemax aria-value max
14667  * @cfg {String} label label for the progress bar
14668  * @cfg {String} panel (success | info | warning | danger )
14669  * @cfg {String} role role of the progress bar
14670  * @cfg {String} sr_only text
14671  * 
14672  * 
14673  * @constructor
14674  * Create a new ProgressBar
14675  * @param {Object} config The config object
14676  */
14677
14678 Roo.bootstrap.ProgressBar = function(config){
14679     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14680 };
14681
14682 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14683     
14684     aria_valuenow : 0,
14685     aria_valuemin : 0,
14686     aria_valuemax : 100,
14687     label : false,
14688     panel : false,
14689     role : false,
14690     sr_only: false,
14691     
14692     getAutoCreate : function()
14693     {
14694         
14695         var cfg = {
14696             tag: 'div',
14697             cls: 'progress-bar',
14698             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14699         };
14700         
14701         if(this.sr_only){
14702             cfg.cn = {
14703                 tag: 'span',
14704                 cls: 'sr-only',
14705                 html: this.sr_only
14706             }
14707         }
14708         
14709         if(this.role){
14710             cfg.role = this.role;
14711         }
14712         
14713         if(this.aria_valuenow){
14714             cfg['aria-valuenow'] = this.aria_valuenow;
14715         }
14716         
14717         if(this.aria_valuemin){
14718             cfg['aria-valuemin'] = this.aria_valuemin;
14719         }
14720         
14721         if(this.aria_valuemax){
14722             cfg['aria-valuemax'] = this.aria_valuemax;
14723         }
14724         
14725         if(this.label && !this.sr_only){
14726             cfg.html = this.label;
14727         }
14728         
14729         if(this.panel){
14730             cfg.cls += ' progress-bar-' + this.panel;
14731         }
14732         
14733         return cfg;
14734     },
14735     
14736     update : function(aria_valuenow)
14737     {
14738         this.aria_valuenow = aria_valuenow;
14739         
14740         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14741     }
14742    
14743 });
14744
14745  
14746
14747  /*
14748  * - LGPL
14749  *
14750  * column
14751  * 
14752  */
14753
14754 /**
14755  * @class Roo.bootstrap.TabGroup
14756  * @extends Roo.bootstrap.Column
14757  * Bootstrap Column class
14758  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14759  * @cfg {Boolean} carousel true to make the group behave like a carousel
14760  * @cfg {Number} bullets show the panel pointer.. default 0
14761  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14762  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14763  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14764  * 
14765  * @constructor
14766  * Create a new TabGroup
14767  * @param {Object} config The config object
14768  */
14769
14770 Roo.bootstrap.TabGroup = function(config){
14771     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14772     if (!this.navId) {
14773         this.navId = Roo.id();
14774     }
14775     this.tabs = [];
14776     Roo.bootstrap.TabGroup.register(this);
14777     
14778 };
14779
14780 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14781     
14782     carousel : false,
14783     transition : false,
14784     bullets : 0,
14785     timer : 0,
14786     autoslide : false,
14787     slideFn : false,
14788     slideOnTouch : false,
14789     
14790     getAutoCreate : function()
14791     {
14792         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14793         
14794         cfg.cls += ' tab-content';
14795         
14796         Roo.log('get auto create...............');
14797         
14798         if (this.carousel) {
14799             cfg.cls += ' carousel slide';
14800             
14801             cfg.cn = [{
14802                cls : 'carousel-inner'
14803             }];
14804         
14805             if(this.bullets > 0 && !Roo.isTouch){
14806                 
14807                 var bullets = {
14808                     cls : 'carousel-bullets',
14809                     cn : []
14810                 };
14811                 
14812                 if(this.bullets_cls){
14813                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14814                 }
14815                 
14816                 for (var i = 0; i < this.bullets; i++){
14817                     bullets.cn.push({
14818                         cls : 'bullet bullet-' + i
14819                     });
14820                 }
14821                 
14822                 bullets.cn.push({
14823                     cls : 'clear'
14824                 });
14825                 
14826                 cfg.cn[0].cn = bullets;
14827             }
14828         }
14829         
14830         return cfg;
14831     },
14832     
14833     initEvents:  function()
14834     {
14835         Roo.log('-------- init events on tab group ---------');
14836         
14837         if(this.bullets > 0 && !Roo.isTouch){
14838             this.initBullet();
14839         }
14840         
14841         Roo.log(this);
14842         
14843         if(Roo.isTouch && this.slideOnTouch){
14844             this.el.on("touchstart", this.onTouchStart, this);
14845         }
14846         
14847         if(this.autoslide){
14848             var _this = this;
14849             
14850             this.slideFn = window.setInterval(function() {
14851                 _this.showPanelNext();
14852             }, this.timer);
14853         }
14854         
14855     },
14856     
14857     onTouchStart : function(e, el, o)
14858     {
14859         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14860             return;
14861         }
14862         
14863         this.showPanelNext();
14864     },
14865     
14866     getChildContainer : function()
14867     {
14868         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14869     },
14870     
14871     /**
14872     * register a Navigation item
14873     * @param {Roo.bootstrap.NavItem} the navitem to add
14874     */
14875     register : function(item)
14876     {
14877         this.tabs.push( item);
14878         item.navId = this.navId; // not really needed..
14879     
14880     },
14881     
14882     getActivePanel : function()
14883     {
14884         var r = false;
14885         Roo.each(this.tabs, function(t) {
14886             if (t.active) {
14887                 r = t;
14888                 return false;
14889             }
14890             return null;
14891         });
14892         return r;
14893         
14894     },
14895     getPanelByName : function(n)
14896     {
14897         var r = false;
14898         Roo.each(this.tabs, function(t) {
14899             if (t.tabId == n) {
14900                 r = t;
14901                 return false;
14902             }
14903             return null;
14904         });
14905         return r;
14906     },
14907     indexOfPanel : function(p)
14908     {
14909         var r = false;
14910         Roo.each(this.tabs, function(t,i) {
14911             if (t.tabId == p.tabId) {
14912                 r = i;
14913                 return false;
14914             }
14915             return null;
14916         });
14917         return r;
14918     },
14919     /**
14920      * show a specific panel
14921      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14922      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14923      */
14924     showPanel : function (pan)
14925     {
14926         if(this.transition){
14927             Roo.log("waiting for the transitionend");
14928             return;
14929         }
14930         
14931         if (typeof(pan) == 'number') {
14932             pan = this.tabs[pan];
14933         }
14934         if (typeof(pan) == 'string') {
14935             pan = this.getPanelByName(pan);
14936         }
14937         if (pan.tabId == this.getActivePanel().tabId) {
14938             return true;
14939         }
14940         var cur = this.getActivePanel();
14941         
14942         if (false === cur.fireEvent('beforedeactivate')) {
14943             return false;
14944         }
14945         
14946         if(this.bullets > 0 && !Roo.isTouch){
14947             this.setActiveBullet(this.indexOfPanel(pan));
14948         }
14949         
14950         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14951             
14952             this.transition = true;
14953             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14954             var lr = dir == 'next' ? 'left' : 'right';
14955             pan.el.addClass(dir); // or prev
14956             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14957             cur.el.addClass(lr); // or right
14958             pan.el.addClass(lr);
14959             
14960             var _this = this;
14961             cur.el.on('transitionend', function() {
14962                 Roo.log("trans end?");
14963                 
14964                 pan.el.removeClass([lr,dir]);
14965                 pan.setActive(true);
14966                 
14967                 cur.el.removeClass([lr]);
14968                 cur.setActive(false);
14969                 
14970                 _this.transition = false;
14971                 
14972             }, this, { single:  true } );
14973             
14974             return true;
14975         }
14976         
14977         cur.setActive(false);
14978         pan.setActive(true);
14979         
14980         return true;
14981         
14982     },
14983     showPanelNext : function()
14984     {
14985         var i = this.indexOfPanel(this.getActivePanel());
14986         
14987         if (i >= this.tabs.length - 1 && !this.autoslide) {
14988             return;
14989         }
14990         
14991         if (i >= this.tabs.length - 1 && this.autoslide) {
14992             i = -1;
14993         }
14994         
14995         this.showPanel(this.tabs[i+1]);
14996     },
14997     
14998     showPanelPrev : function()
14999     {
15000         var i = this.indexOfPanel(this.getActivePanel());
15001         
15002         if (i  < 1 && !this.autoslide) {
15003             return;
15004         }
15005         
15006         if (i < 1 && this.autoslide) {
15007             i = this.tabs.length;
15008         }
15009         
15010         this.showPanel(this.tabs[i-1]);
15011     },
15012     
15013     initBullet : function()
15014     {
15015         if(Roo.isTouch){
15016             return;
15017         }
15018         
15019         var _this = this;
15020         
15021         for (var i = 0; i < this.bullets; i++){
15022             var bullet = this.el.select('.bullet-' + i, true).first();
15023
15024             if(!bullet){
15025                 continue;
15026             }
15027
15028             bullet.on('click', (function(e, el, o, ii, t){
15029
15030                 e.preventDefault();
15031
15032                 _this.showPanel(ii);
15033
15034                 if(_this.autoslide && _this.slideFn){
15035                     clearInterval(_this.slideFn);
15036                     _this.slideFn = window.setInterval(function() {
15037                         _this.showPanelNext();
15038                     }, _this.timer);
15039                 }
15040
15041             }).createDelegate(this, [i, bullet], true));
15042         }
15043     },
15044     
15045     setActiveBullet : function(i)
15046     {
15047         if(Roo.isTouch){
15048             return;
15049         }
15050         
15051         Roo.each(this.el.select('.bullet', true).elements, function(el){
15052             el.removeClass('selected');
15053         });
15054
15055         var bullet = this.el.select('.bullet-' + i, true).first();
15056         
15057         if(!bullet){
15058             return;
15059         }
15060         
15061         bullet.addClass('selected');
15062     }
15063     
15064     
15065   
15066 });
15067
15068  
15069
15070  
15071  
15072 Roo.apply(Roo.bootstrap.TabGroup, {
15073     
15074     groups: {},
15075      /**
15076     * register a Navigation Group
15077     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15078     */
15079     register : function(navgrp)
15080     {
15081         this.groups[navgrp.navId] = navgrp;
15082         
15083     },
15084     /**
15085     * fetch a Navigation Group based on the navigation ID
15086     * if one does not exist , it will get created.
15087     * @param {string} the navgroup to add
15088     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15089     */
15090     get: function(navId) {
15091         if (typeof(this.groups[navId]) == 'undefined') {
15092             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15093         }
15094         return this.groups[navId] ;
15095     }
15096     
15097     
15098     
15099 });
15100
15101  /*
15102  * - LGPL
15103  *
15104  * TabPanel
15105  * 
15106  */
15107
15108 /**
15109  * @class Roo.bootstrap.TabPanel
15110  * @extends Roo.bootstrap.Component
15111  * Bootstrap TabPanel class
15112  * @cfg {Boolean} active panel active
15113  * @cfg {String} html panel content
15114  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15115  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15116  * 
15117  * 
15118  * @constructor
15119  * Create a new TabPanel
15120  * @param {Object} config The config object
15121  */
15122
15123 Roo.bootstrap.TabPanel = function(config){
15124     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15125     this.addEvents({
15126         /**
15127              * @event changed
15128              * Fires when the active status changes
15129              * @param {Roo.bootstrap.TabPanel} this
15130              * @param {Boolean} state the new state
15131             
15132          */
15133         'changed': true,
15134         /**
15135              * @event beforedeactivate
15136              * Fires before a tab is de-activated - can be used to do validation on a form.
15137              * @param {Roo.bootstrap.TabPanel} this
15138              * @return {Boolean} false if there is an error
15139             
15140          */
15141         'beforedeactivate': true
15142      });
15143     
15144     this.tabId = this.tabId || Roo.id();
15145   
15146 };
15147
15148 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15149     
15150     active: false,
15151     html: false,
15152     tabId: false,
15153     navId : false,
15154     
15155     getAutoCreate : function(){
15156         var cfg = {
15157             tag: 'div',
15158             // item is needed for carousel - not sure if it has any effect otherwise
15159             cls: 'tab-pane item',
15160             html: this.html || ''
15161         };
15162         
15163         if(this.active){
15164             cfg.cls += ' active';
15165         }
15166         
15167         if(this.tabId){
15168             cfg.tabId = this.tabId;
15169         }
15170         
15171         
15172         return cfg;
15173     },
15174     
15175     initEvents:  function()
15176     {
15177         Roo.log('-------- init events on tab panel ---------');
15178         
15179         var p = this.parent();
15180         this.navId = this.navId || p.navId;
15181         
15182         if (typeof(this.navId) != 'undefined') {
15183             // not really needed.. but just in case.. parent should be a NavGroup.
15184             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15185             Roo.log(['register', tg, this]);
15186             tg.register(this);
15187             
15188             var i = tg.tabs.length - 1;
15189             
15190             if(this.active && tg.bullets > 0 && i < tg.bullets){
15191                 tg.setActiveBullet(i);
15192             }
15193         }
15194         
15195     },
15196     
15197     
15198     onRender : function(ct, position)
15199     {
15200        // Roo.log("Call onRender: " + this.xtype);
15201         
15202         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15203         
15204         
15205         
15206         
15207         
15208     },
15209     
15210     setActive: function(state)
15211     {
15212         Roo.log("panel - set active " + this.tabId + "=" + state);
15213         
15214         this.active = state;
15215         if (!state) {
15216             this.el.removeClass('active');
15217             
15218         } else  if (!this.el.hasClass('active')) {
15219             this.el.addClass('active');
15220         }
15221         
15222         this.fireEvent('changed', this, state);
15223     }
15224     
15225     
15226 });
15227  
15228
15229  
15230
15231  /*
15232  * - LGPL
15233  *
15234  * DateField
15235  * 
15236  */
15237
15238 /**
15239  * @class Roo.bootstrap.DateField
15240  * @extends Roo.bootstrap.Input
15241  * Bootstrap DateField class
15242  * @cfg {Number} weekStart default 0
15243  * @cfg {String} viewMode default empty, (months|years)
15244  * @cfg {String} minViewMode default empty, (months|years)
15245  * @cfg {Number} startDate default -Infinity
15246  * @cfg {Number} endDate default Infinity
15247  * @cfg {Boolean} todayHighlight default false
15248  * @cfg {Boolean} todayBtn default false
15249  * @cfg {Boolean} calendarWeeks default false
15250  * @cfg {Object} daysOfWeekDisabled default empty
15251  * @cfg {Boolean} singleMode default false (true | false)
15252  * 
15253  * @cfg {Boolean} keyboardNavigation default true
15254  * @cfg {String} language default en
15255  * 
15256  * @constructor
15257  * Create a new DateField
15258  * @param {Object} config The config object
15259  */
15260
15261 Roo.bootstrap.DateField = function(config){
15262     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15263      this.addEvents({
15264             /**
15265              * @event show
15266              * Fires when this field show.
15267              * @param {Roo.bootstrap.DateField} this
15268              * @param {Mixed} date The date value
15269              */
15270             show : true,
15271             /**
15272              * @event show
15273              * Fires when this field hide.
15274              * @param {Roo.bootstrap.DateField} this
15275              * @param {Mixed} date The date value
15276              */
15277             hide : true,
15278             /**
15279              * @event select
15280              * Fires when select a date.
15281              * @param {Roo.bootstrap.DateField} this
15282              * @param {Mixed} date The date value
15283              */
15284             select : true
15285         });
15286 };
15287
15288 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15289     
15290     /**
15291      * @cfg {String} format
15292      * The default date format string which can be overriden for localization support.  The format must be
15293      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15294      */
15295     format : "m/d/y",
15296     /**
15297      * @cfg {String} altFormats
15298      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15299      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15300      */
15301     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15302     
15303     weekStart : 0,
15304     
15305     viewMode : '',
15306     
15307     minViewMode : '',
15308     
15309     todayHighlight : false,
15310     
15311     todayBtn: false,
15312     
15313     language: 'en',
15314     
15315     keyboardNavigation: true,
15316     
15317     calendarWeeks: false,
15318     
15319     startDate: -Infinity,
15320     
15321     endDate: Infinity,
15322     
15323     daysOfWeekDisabled: [],
15324     
15325     _events: [],
15326     
15327     singleMode : false,
15328     
15329     UTCDate: function()
15330     {
15331         return new Date(Date.UTC.apply(Date, arguments));
15332     },
15333     
15334     UTCToday: function()
15335     {
15336         var today = new Date();
15337         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15338     },
15339     
15340     getDate: function() {
15341             var d = this.getUTCDate();
15342             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15343     },
15344     
15345     getUTCDate: function() {
15346             return this.date;
15347     },
15348     
15349     setDate: function(d) {
15350             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15351     },
15352     
15353     setUTCDate: function(d) {
15354             this.date = d;
15355             this.setValue(this.formatDate(this.date));
15356     },
15357         
15358     onRender: function(ct, position)
15359     {
15360         
15361         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15362         
15363         this.language = this.language || 'en';
15364         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15365         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15366         
15367         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15368         this.format = this.format || 'm/d/y';
15369         this.isInline = false;
15370         this.isInput = true;
15371         this.component = this.el.select('.add-on', true).first() || false;
15372         this.component = (this.component && this.component.length === 0) ? false : this.component;
15373         this.hasInput = this.component && this.inputEL().length;
15374         
15375         if (typeof(this.minViewMode === 'string')) {
15376             switch (this.minViewMode) {
15377                 case 'months':
15378                     this.minViewMode = 1;
15379                     break;
15380                 case 'years':
15381                     this.minViewMode = 2;
15382                     break;
15383                 default:
15384                     this.minViewMode = 0;
15385                     break;
15386             }
15387         }
15388         
15389         if (typeof(this.viewMode === 'string')) {
15390             switch (this.viewMode) {
15391                 case 'months':
15392                     this.viewMode = 1;
15393                     break;
15394                 case 'years':
15395                     this.viewMode = 2;
15396                     break;
15397                 default:
15398                     this.viewMode = 0;
15399                     break;
15400             }
15401         }
15402                 
15403         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15404         
15405 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15406         
15407         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15408         
15409         this.picker().on('mousedown', this.onMousedown, this);
15410         this.picker().on('click', this.onClick, this);
15411         
15412         this.picker().addClass('datepicker-dropdown');
15413         
15414         this.startViewMode = this.viewMode;
15415         
15416         if(this.singleMode){
15417             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15418                 v.setVisibilityMode(Roo.Element.DISPLAY)
15419                 v.hide();
15420             });
15421             
15422             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15423                 v.setStyle('width', '189px');
15424             });
15425         }
15426         
15427         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15428             if(!this.calendarWeeks){
15429                 v.remove();
15430                 return;
15431             }
15432             
15433             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15434             v.attr('colspan', function(i, val){
15435                 return parseInt(val) + 1;
15436             });
15437         })
15438                         
15439         
15440         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15441         
15442         this.setStartDate(this.startDate);
15443         this.setEndDate(this.endDate);
15444         
15445         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15446         
15447         this.fillDow();
15448         this.fillMonths();
15449         this.update();
15450         this.showMode();
15451         
15452         if(this.isInline) {
15453             this.show();
15454         }
15455     },
15456     
15457     picker : function()
15458     {
15459         return this.pickerEl;
15460 //        return this.el.select('.datepicker', true).first();
15461     },
15462     
15463     fillDow: function()
15464     {
15465         var dowCnt = this.weekStart;
15466         
15467         var dow = {
15468             tag: 'tr',
15469             cn: [
15470                 
15471             ]
15472         };
15473         
15474         if(this.calendarWeeks){
15475             dow.cn.push({
15476                 tag: 'th',
15477                 cls: 'cw',
15478                 html: '&nbsp;'
15479             })
15480         }
15481         
15482         while (dowCnt < this.weekStart + 7) {
15483             dow.cn.push({
15484                 tag: 'th',
15485                 cls: 'dow',
15486                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15487             });
15488         }
15489         
15490         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15491     },
15492     
15493     fillMonths: function()
15494     {    
15495         var i = 0;
15496         var months = this.picker().select('>.datepicker-months td', true).first();
15497         
15498         months.dom.innerHTML = '';
15499         
15500         while (i < 12) {
15501             var month = {
15502                 tag: 'span',
15503                 cls: 'month',
15504                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15505             }
15506             
15507             months.createChild(month);
15508         }
15509         
15510     },
15511     
15512     update: function()
15513     {
15514         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;
15515         
15516         if (this.date < this.startDate) {
15517             this.viewDate = new Date(this.startDate);
15518         } else if (this.date > this.endDate) {
15519             this.viewDate = new Date(this.endDate);
15520         } else {
15521             this.viewDate = new Date(this.date);
15522         }
15523         
15524         this.fill();
15525     },
15526     
15527     fill: function() 
15528     {
15529         var d = new Date(this.viewDate),
15530                 year = d.getUTCFullYear(),
15531                 month = d.getUTCMonth(),
15532                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15533                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15534                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15535                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15536                 currentDate = this.date && this.date.valueOf(),
15537                 today = this.UTCToday();
15538         
15539         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15540         
15541 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15542         
15543 //        this.picker.select('>tfoot th.today').
15544 //                                              .text(dates[this.language].today)
15545 //                                              .toggle(this.todayBtn !== false);
15546     
15547         this.updateNavArrows();
15548         this.fillMonths();
15549                                                 
15550         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15551         
15552         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15553          
15554         prevMonth.setUTCDate(day);
15555         
15556         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15557         
15558         var nextMonth = new Date(prevMonth);
15559         
15560         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15561         
15562         nextMonth = nextMonth.valueOf();
15563         
15564         var fillMonths = false;
15565         
15566         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15567         
15568         while(prevMonth.valueOf() < nextMonth) {
15569             var clsName = '';
15570             
15571             if (prevMonth.getUTCDay() === this.weekStart) {
15572                 if(fillMonths){
15573                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15574                 }
15575                     
15576                 fillMonths = {
15577                     tag: 'tr',
15578                     cn: []
15579                 };
15580                 
15581                 if(this.calendarWeeks){
15582                     // ISO 8601: First week contains first thursday.
15583                     // ISO also states week starts on Monday, but we can be more abstract here.
15584                     var
15585                     // Start of current week: based on weekstart/current date
15586                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15587                     // Thursday of this week
15588                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15589                     // First Thursday of year, year from thursday
15590                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15591                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15592                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15593                     
15594                     fillMonths.cn.push({
15595                         tag: 'td',
15596                         cls: 'cw',
15597                         html: calWeek
15598                     });
15599                 }
15600             }
15601             
15602             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15603                 clsName += ' old';
15604             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15605                 clsName += ' new';
15606             }
15607             if (this.todayHighlight &&
15608                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15609                 prevMonth.getUTCMonth() == today.getMonth() &&
15610                 prevMonth.getUTCDate() == today.getDate()) {
15611                 clsName += ' today';
15612             }
15613             
15614             if (currentDate && prevMonth.valueOf() === currentDate) {
15615                 clsName += ' active';
15616             }
15617             
15618             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15619                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15620                     clsName += ' disabled';
15621             }
15622             
15623             fillMonths.cn.push({
15624                 tag: 'td',
15625                 cls: 'day ' + clsName,
15626                 html: prevMonth.getDate()
15627             })
15628             
15629             prevMonth.setDate(prevMonth.getDate()+1);
15630         }
15631           
15632         var currentYear = this.date && this.date.getUTCFullYear();
15633         var currentMonth = this.date && this.date.getUTCMonth();
15634         
15635         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15636         
15637         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15638             v.removeClass('active');
15639             
15640             if(currentYear === year && k === currentMonth){
15641                 v.addClass('active');
15642             }
15643             
15644             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15645                 v.addClass('disabled');
15646             }
15647             
15648         });
15649         
15650         
15651         year = parseInt(year/10, 10) * 10;
15652         
15653         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15654         
15655         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15656         
15657         year -= 1;
15658         for (var i = -1; i < 11; i++) {
15659             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15660                 tag: 'span',
15661                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15662                 html: year
15663             })
15664             
15665             year += 1;
15666         }
15667     },
15668     
15669     showMode: function(dir) 
15670     {
15671         if (dir) {
15672             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15673         }
15674         
15675         Roo.each(this.picker().select('>div',true).elements, function(v){
15676             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15677             v.hide();
15678         });
15679         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15680     },
15681     
15682     place: function()
15683     {
15684         if(this.isInline) return;
15685         
15686         this.picker().removeClass(['bottom', 'top']);
15687         
15688         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15689             /*
15690              * place to the top of element!
15691              *
15692              */
15693             
15694             this.picker().addClass('top');
15695             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15696             
15697             return;
15698         }
15699         
15700         this.picker().addClass('bottom');
15701         
15702         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15703     },
15704     
15705     parseDate : function(value)
15706     {
15707         if(!value || value instanceof Date){
15708             return value;
15709         }
15710         var v = Date.parseDate(value, this.format);
15711         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15712             v = Date.parseDate(value, 'Y-m-d');
15713         }
15714         if(!v && this.altFormats){
15715             if(!this.altFormatsArray){
15716                 this.altFormatsArray = this.altFormats.split("|");
15717             }
15718             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15719                 v = Date.parseDate(value, this.altFormatsArray[i]);
15720             }
15721         }
15722         return v;
15723     },
15724     
15725     formatDate : function(date, fmt)
15726     {   
15727         return (!date || !(date instanceof Date)) ?
15728         date : date.dateFormat(fmt || this.format);
15729     },
15730     
15731     onFocus : function()
15732     {
15733         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15734         this.show();
15735     },
15736     
15737     onBlur : function()
15738     {
15739         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15740         
15741         var d = this.inputEl().getValue();
15742         
15743         this.setValue(d);
15744                 
15745         this.hide();
15746     },
15747     
15748     show : function()
15749     {
15750         this.picker().show();
15751         this.update();
15752         this.place();
15753         
15754         this.fireEvent('show', this, this.date);
15755     },
15756     
15757     hide : function()
15758     {
15759         if(this.isInline) return;
15760         this.picker().hide();
15761         this.viewMode = this.startViewMode;
15762         this.showMode();
15763         
15764         this.fireEvent('hide', this, this.date);
15765         
15766     },
15767     
15768     onMousedown: function(e)
15769     {
15770         e.stopPropagation();
15771         e.preventDefault();
15772     },
15773     
15774     keyup: function(e)
15775     {
15776         Roo.bootstrap.DateField.superclass.keyup.call(this);
15777         this.update();
15778     },
15779
15780     setValue: function(v)
15781     {
15782         
15783         // v can be a string or a date..
15784         
15785         
15786         var d = new Date(this.parseDate(v) ).clearTime();
15787         
15788         if(isNaN(d.getTime())){
15789             this.date = this.viewDate = '';
15790             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15791             return;
15792         }
15793         
15794         v = this.formatDate(d);
15795         
15796         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15797         
15798         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15799      
15800         this.update();
15801
15802         this.fireEvent('select', this, this.date);
15803         
15804     },
15805     
15806     getValue: function()
15807     {
15808         return this.formatDate(this.date);
15809     },
15810     
15811     fireKey: function(e)
15812     {
15813         if (!this.picker().isVisible()){
15814             if (e.keyCode == 27) // allow escape to hide and re-show picker
15815                 this.show();
15816             return;
15817         }
15818         
15819         var dateChanged = false,
15820         dir, day, month,
15821         newDate, newViewDate;
15822         
15823         switch(e.keyCode){
15824             case 27: // escape
15825                 this.hide();
15826                 e.preventDefault();
15827                 break;
15828             case 37: // left
15829             case 39: // right
15830                 if (!this.keyboardNavigation) break;
15831                 dir = e.keyCode == 37 ? -1 : 1;
15832                 
15833                 if (e.ctrlKey){
15834                     newDate = this.moveYear(this.date, dir);
15835                     newViewDate = this.moveYear(this.viewDate, dir);
15836                 } else if (e.shiftKey){
15837                     newDate = this.moveMonth(this.date, dir);
15838                     newViewDate = this.moveMonth(this.viewDate, dir);
15839                 } else {
15840                     newDate = new Date(this.date);
15841                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15842                     newViewDate = new Date(this.viewDate);
15843                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15844                 }
15845                 if (this.dateWithinRange(newDate)){
15846                     this.date = newDate;
15847                     this.viewDate = newViewDate;
15848                     this.setValue(this.formatDate(this.date));
15849 //                    this.update();
15850                     e.preventDefault();
15851                     dateChanged = true;
15852                 }
15853                 break;
15854             case 38: // up
15855             case 40: // down
15856                 if (!this.keyboardNavigation) break;
15857                 dir = e.keyCode == 38 ? -1 : 1;
15858                 if (e.ctrlKey){
15859                     newDate = this.moveYear(this.date, dir);
15860                     newViewDate = this.moveYear(this.viewDate, dir);
15861                 } else if (e.shiftKey){
15862                     newDate = this.moveMonth(this.date, dir);
15863                     newViewDate = this.moveMonth(this.viewDate, dir);
15864                 } else {
15865                     newDate = new Date(this.date);
15866                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15867                     newViewDate = new Date(this.viewDate);
15868                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15869                 }
15870                 if (this.dateWithinRange(newDate)){
15871                     this.date = newDate;
15872                     this.viewDate = newViewDate;
15873                     this.setValue(this.formatDate(this.date));
15874 //                    this.update();
15875                     e.preventDefault();
15876                     dateChanged = true;
15877                 }
15878                 break;
15879             case 13: // enter
15880                 this.setValue(this.formatDate(this.date));
15881                 this.hide();
15882                 e.preventDefault();
15883                 break;
15884             case 9: // tab
15885                 this.setValue(this.formatDate(this.date));
15886                 this.hide();
15887                 break;
15888             case 16: // shift
15889             case 17: // ctrl
15890             case 18: // alt
15891                 break;
15892             default :
15893                 this.hide();
15894                 
15895         }
15896     },
15897     
15898     
15899     onClick: function(e) 
15900     {
15901         e.stopPropagation();
15902         e.preventDefault();
15903         
15904         var target = e.getTarget();
15905         
15906         if(target.nodeName.toLowerCase() === 'i'){
15907             target = Roo.get(target).dom.parentNode;
15908         }
15909         
15910         var nodeName = target.nodeName;
15911         var className = target.className;
15912         var html = target.innerHTML;
15913         //Roo.log(nodeName);
15914         
15915         switch(nodeName.toLowerCase()) {
15916             case 'th':
15917                 switch(className) {
15918                     case 'switch':
15919                         this.showMode(1);
15920                         break;
15921                     case 'prev':
15922                     case 'next':
15923                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15924                         switch(this.viewMode){
15925                                 case 0:
15926                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15927                                         break;
15928                                 case 1:
15929                                 case 2:
15930                                         this.viewDate = this.moveYear(this.viewDate, dir);
15931                                         break;
15932                         }
15933                         this.fill();
15934                         break;
15935                     case 'today':
15936                         var date = new Date();
15937                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15938 //                        this.fill()
15939                         this.setValue(this.formatDate(this.date));
15940                         
15941                         this.hide();
15942                         break;
15943                 }
15944                 break;
15945             case 'span':
15946                 if (className.indexOf('disabled') < 0) {
15947                     this.viewDate.setUTCDate(1);
15948                     if (className.indexOf('month') > -1) {
15949                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15950                     } else {
15951                         var year = parseInt(html, 10) || 0;
15952                         this.viewDate.setUTCFullYear(year);
15953                         
15954                     }
15955                     
15956                     if(this.singleMode){
15957                         this.setValue(this.formatDate(this.viewDate));
15958                         this.hide();
15959                         return;
15960                     }
15961                     
15962                     this.showMode(-1);
15963                     this.fill();
15964                 }
15965                 break;
15966                 
15967             case 'td':
15968                 //Roo.log(className);
15969                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15970                     var day = parseInt(html, 10) || 1;
15971                     var year = this.viewDate.getUTCFullYear(),
15972                         month = this.viewDate.getUTCMonth();
15973
15974                     if (className.indexOf('old') > -1) {
15975                         if(month === 0 ){
15976                             month = 11;
15977                             year -= 1;
15978                         }else{
15979                             month -= 1;
15980                         }
15981                     } else if (className.indexOf('new') > -1) {
15982                         if (month == 11) {
15983                             month = 0;
15984                             year += 1;
15985                         } else {
15986                             month += 1;
15987                         }
15988                     }
15989                     //Roo.log([year,month,day]);
15990                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15991                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15992 //                    this.fill();
15993                     //Roo.log(this.formatDate(this.date));
15994                     this.setValue(this.formatDate(this.date));
15995                     this.hide();
15996                 }
15997                 break;
15998         }
15999     },
16000     
16001     setStartDate: function(startDate)
16002     {
16003         this.startDate = startDate || -Infinity;
16004         if (this.startDate !== -Infinity) {
16005             this.startDate = this.parseDate(this.startDate);
16006         }
16007         this.update();
16008         this.updateNavArrows();
16009     },
16010
16011     setEndDate: function(endDate)
16012     {
16013         this.endDate = endDate || Infinity;
16014         if (this.endDate !== Infinity) {
16015             this.endDate = this.parseDate(this.endDate);
16016         }
16017         this.update();
16018         this.updateNavArrows();
16019     },
16020     
16021     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16022     {
16023         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16024         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16025             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16026         }
16027         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16028             return parseInt(d, 10);
16029         });
16030         this.update();
16031         this.updateNavArrows();
16032     },
16033     
16034     updateNavArrows: function() 
16035     {
16036         if(this.singleMode){
16037             return;
16038         }
16039         
16040         var d = new Date(this.viewDate),
16041         year = d.getUTCFullYear(),
16042         month = d.getUTCMonth();
16043         
16044         Roo.each(this.picker().select('.prev', true).elements, function(v){
16045             v.show();
16046             switch (this.viewMode) {
16047                 case 0:
16048
16049                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16050                         v.hide();
16051                     }
16052                     break;
16053                 case 1:
16054                 case 2:
16055                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16056                         v.hide();
16057                     }
16058                     break;
16059             }
16060         });
16061         
16062         Roo.each(this.picker().select('.next', true).elements, function(v){
16063             v.show();
16064             switch (this.viewMode) {
16065                 case 0:
16066
16067                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16068                         v.hide();
16069                     }
16070                     break;
16071                 case 1:
16072                 case 2:
16073                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16074                         v.hide();
16075                     }
16076                     break;
16077             }
16078         })
16079     },
16080     
16081     moveMonth: function(date, dir)
16082     {
16083         if (!dir) return date;
16084         var new_date = new Date(date.valueOf()),
16085         day = new_date.getUTCDate(),
16086         month = new_date.getUTCMonth(),
16087         mag = Math.abs(dir),
16088         new_month, test;
16089         dir = dir > 0 ? 1 : -1;
16090         if (mag == 1){
16091             test = dir == -1
16092             // If going back one month, make sure month is not current month
16093             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16094             ? function(){
16095                 return new_date.getUTCMonth() == month;
16096             }
16097             // If going forward one month, make sure month is as expected
16098             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16099             : function(){
16100                 return new_date.getUTCMonth() != new_month;
16101             };
16102             new_month = month + dir;
16103             new_date.setUTCMonth(new_month);
16104             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16105             if (new_month < 0 || new_month > 11)
16106                 new_month = (new_month + 12) % 12;
16107         } else {
16108             // For magnitudes >1, move one month at a time...
16109             for (var i=0; i<mag; i++)
16110                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16111                 new_date = this.moveMonth(new_date, dir);
16112             // ...then reset the day, keeping it in the new month
16113             new_month = new_date.getUTCMonth();
16114             new_date.setUTCDate(day);
16115             test = function(){
16116                 return new_month != new_date.getUTCMonth();
16117             };
16118         }
16119         // Common date-resetting loop -- if date is beyond end of month, make it
16120         // end of month
16121         while (test()){
16122             new_date.setUTCDate(--day);
16123             new_date.setUTCMonth(new_month);
16124         }
16125         return new_date;
16126     },
16127
16128     moveYear: function(date, dir)
16129     {
16130         return this.moveMonth(date, dir*12);
16131     },
16132
16133     dateWithinRange: function(date)
16134     {
16135         return date >= this.startDate && date <= this.endDate;
16136     },
16137
16138     
16139     remove: function() 
16140     {
16141         this.picker().remove();
16142     }
16143    
16144 });
16145
16146 Roo.apply(Roo.bootstrap.DateField,  {
16147     
16148     head : {
16149         tag: 'thead',
16150         cn: [
16151         {
16152             tag: 'tr',
16153             cn: [
16154             {
16155                 tag: 'th',
16156                 cls: 'prev',
16157                 html: '<i class="fa fa-arrow-left"/>'
16158             },
16159             {
16160                 tag: 'th',
16161                 cls: 'switch',
16162                 colspan: '5'
16163             },
16164             {
16165                 tag: 'th',
16166                 cls: 'next',
16167                 html: '<i class="fa fa-arrow-right"/>'
16168             }
16169
16170             ]
16171         }
16172         ]
16173     },
16174     
16175     content : {
16176         tag: 'tbody',
16177         cn: [
16178         {
16179             tag: 'tr',
16180             cn: [
16181             {
16182                 tag: 'td',
16183                 colspan: '7'
16184             }
16185             ]
16186         }
16187         ]
16188     },
16189     
16190     footer : {
16191         tag: 'tfoot',
16192         cn: [
16193         {
16194             tag: 'tr',
16195             cn: [
16196             {
16197                 tag: 'th',
16198                 colspan: '7',
16199                 cls: 'today'
16200             }
16201                     
16202             ]
16203         }
16204         ]
16205     },
16206     
16207     dates:{
16208         en: {
16209             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16210             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16211             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16212             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16213             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16214             today: "Today"
16215         }
16216     },
16217     
16218     modes: [
16219     {
16220         clsName: 'days',
16221         navFnc: 'Month',
16222         navStep: 1
16223     },
16224     {
16225         clsName: 'months',
16226         navFnc: 'FullYear',
16227         navStep: 1
16228     },
16229     {
16230         clsName: 'years',
16231         navFnc: 'FullYear',
16232         navStep: 10
16233     }]
16234 });
16235
16236 Roo.apply(Roo.bootstrap.DateField,  {
16237   
16238     template : {
16239         tag: 'div',
16240         cls: 'datepicker dropdown-menu roo-dynamic',
16241         cn: [
16242         {
16243             tag: 'div',
16244             cls: 'datepicker-days',
16245             cn: [
16246             {
16247                 tag: 'table',
16248                 cls: 'table-condensed',
16249                 cn:[
16250                 Roo.bootstrap.DateField.head,
16251                 {
16252                     tag: 'tbody'
16253                 },
16254                 Roo.bootstrap.DateField.footer
16255                 ]
16256             }
16257             ]
16258         },
16259         {
16260             tag: 'div',
16261             cls: 'datepicker-months',
16262             cn: [
16263             {
16264                 tag: 'table',
16265                 cls: 'table-condensed',
16266                 cn:[
16267                 Roo.bootstrap.DateField.head,
16268                 Roo.bootstrap.DateField.content,
16269                 Roo.bootstrap.DateField.footer
16270                 ]
16271             }
16272             ]
16273         },
16274         {
16275             tag: 'div',
16276             cls: 'datepicker-years',
16277             cn: [
16278             {
16279                 tag: 'table',
16280                 cls: 'table-condensed',
16281                 cn:[
16282                 Roo.bootstrap.DateField.head,
16283                 Roo.bootstrap.DateField.content,
16284                 Roo.bootstrap.DateField.footer
16285                 ]
16286             }
16287             ]
16288         }
16289         ]
16290     }
16291 });
16292
16293  
16294
16295  /*
16296  * - LGPL
16297  *
16298  * TimeField
16299  * 
16300  */
16301
16302 /**
16303  * @class Roo.bootstrap.TimeField
16304  * @extends Roo.bootstrap.Input
16305  * Bootstrap DateField class
16306  * 
16307  * 
16308  * @constructor
16309  * Create a new TimeField
16310  * @param {Object} config The config object
16311  */
16312
16313 Roo.bootstrap.TimeField = function(config){
16314     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16315     this.addEvents({
16316             /**
16317              * @event show
16318              * Fires when this field show.
16319              * @param {Roo.bootstrap.DateField} thisthis
16320              * @param {Mixed} date The date value
16321              */
16322             show : true,
16323             /**
16324              * @event show
16325              * Fires when this field hide.
16326              * @param {Roo.bootstrap.DateField} this
16327              * @param {Mixed} date The date value
16328              */
16329             hide : true,
16330             /**
16331              * @event select
16332              * Fires when select a date.
16333              * @param {Roo.bootstrap.DateField} this
16334              * @param {Mixed} date The date value
16335              */
16336             select : true
16337         });
16338 };
16339
16340 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16341     
16342     /**
16343      * @cfg {String} format
16344      * The default time format string which can be overriden for localization support.  The format must be
16345      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16346      */
16347     format : "H:i",
16348        
16349     onRender: function(ct, position)
16350     {
16351         
16352         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16353                 
16354         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16355         
16356         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16357         
16358         this.pop = this.picker().select('>.datepicker-time',true).first();
16359         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16360         
16361         this.picker().on('mousedown', this.onMousedown, this);
16362         this.picker().on('click', this.onClick, this);
16363         
16364         this.picker().addClass('datepicker-dropdown');
16365     
16366         this.fillTime();
16367         this.update();
16368             
16369         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16370         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16371         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16372         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16373         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16374         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16375
16376     },
16377     
16378     fireKey: function(e){
16379         if (!this.picker().isVisible()){
16380             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16381                 this.show();
16382             }
16383             return;
16384         }
16385
16386         e.preventDefault();
16387         
16388         switch(e.keyCode){
16389             case 27: // escape
16390                 this.hide();
16391                 break;
16392             case 37: // left
16393             case 39: // right
16394                 this.onTogglePeriod();
16395                 break;
16396             case 38: // up
16397                 this.onIncrementMinutes();
16398                 break;
16399             case 40: // down
16400                 this.onDecrementMinutes();
16401                 break;
16402             case 13: // enter
16403             case 9: // tab
16404                 this.setTime();
16405                 break;
16406         }
16407     },
16408     
16409     onClick: function(e) {
16410         e.stopPropagation();
16411         e.preventDefault();
16412     },
16413     
16414     picker : function()
16415     {
16416         return this.el.select('.datepicker', true).first();
16417     },
16418     
16419     fillTime: function()
16420     {    
16421         var time = this.pop.select('tbody', true).first();
16422         
16423         time.dom.innerHTML = '';
16424         
16425         time.createChild({
16426             tag: 'tr',
16427             cn: [
16428                 {
16429                     tag: 'td',
16430                     cn: [
16431                         {
16432                             tag: 'a',
16433                             href: '#',
16434                             cls: 'btn',
16435                             cn: [
16436                                 {
16437                                     tag: 'span',
16438                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16439                                 }
16440                             ]
16441                         } 
16442                     ]
16443                 },
16444                 {
16445                     tag: 'td',
16446                     cls: 'separator'
16447                 },
16448                 {
16449                     tag: 'td',
16450                     cn: [
16451                         {
16452                             tag: 'a',
16453                             href: '#',
16454                             cls: 'btn',
16455                             cn: [
16456                                 {
16457                                     tag: 'span',
16458                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16459                                 }
16460                             ]
16461                         }
16462                     ]
16463                 },
16464                 {
16465                     tag: 'td',
16466                     cls: 'separator'
16467                 }
16468             ]
16469         });
16470         
16471         time.createChild({
16472             tag: 'tr',
16473             cn: [
16474                 {
16475                     tag: 'td',
16476                     cn: [
16477                         {
16478                             tag: 'span',
16479                             cls: 'timepicker-hour',
16480                             html: '00'
16481                         }  
16482                     ]
16483                 },
16484                 {
16485                     tag: 'td',
16486                     cls: 'separator',
16487                     html: ':'
16488                 },
16489                 {
16490                     tag: 'td',
16491                     cn: [
16492                         {
16493                             tag: 'span',
16494                             cls: 'timepicker-minute',
16495                             html: '00'
16496                         }  
16497                     ]
16498                 },
16499                 {
16500                     tag: 'td',
16501                     cls: 'separator'
16502                 },
16503                 {
16504                     tag: 'td',
16505                     cn: [
16506                         {
16507                             tag: 'button',
16508                             type: 'button',
16509                             cls: 'btn btn-primary period',
16510                             html: 'AM'
16511                             
16512                         }
16513                     ]
16514                 }
16515             ]
16516         });
16517         
16518         time.createChild({
16519             tag: 'tr',
16520             cn: [
16521                 {
16522                     tag: 'td',
16523                     cn: [
16524                         {
16525                             tag: 'a',
16526                             href: '#',
16527                             cls: 'btn',
16528                             cn: [
16529                                 {
16530                                     tag: 'span',
16531                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16532                                 }
16533                             ]
16534                         }
16535                     ]
16536                 },
16537                 {
16538                     tag: 'td',
16539                     cls: 'separator'
16540                 },
16541                 {
16542                     tag: 'td',
16543                     cn: [
16544                         {
16545                             tag: 'a',
16546                             href: '#',
16547                             cls: 'btn',
16548                             cn: [
16549                                 {
16550                                     tag: 'span',
16551                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16552                                 }
16553                             ]
16554                         }
16555                     ]
16556                 },
16557                 {
16558                     tag: 'td',
16559                     cls: 'separator'
16560                 }
16561             ]
16562         });
16563         
16564     },
16565     
16566     update: function()
16567     {
16568         
16569         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16570         
16571         this.fill();
16572     },
16573     
16574     fill: function() 
16575     {
16576         var hours = this.time.getHours();
16577         var minutes = this.time.getMinutes();
16578         var period = 'AM';
16579         
16580         if(hours > 11){
16581             period = 'PM';
16582         }
16583         
16584         if(hours == 0){
16585             hours = 12;
16586         }
16587         
16588         
16589         if(hours > 12){
16590             hours = hours - 12;
16591         }
16592         
16593         if(hours < 10){
16594             hours = '0' + hours;
16595         }
16596         
16597         if(minutes < 10){
16598             minutes = '0' + minutes;
16599         }
16600         
16601         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16602         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16603         this.pop.select('button', true).first().dom.innerHTML = period;
16604         
16605     },
16606     
16607     place: function()
16608     {   
16609         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16610         
16611         var cls = ['bottom'];
16612         
16613         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16614             cls.pop();
16615             cls.push('top');
16616         }
16617         
16618         cls.push('right');
16619         
16620         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16621             cls.pop();
16622             cls.push('left');
16623         }
16624         
16625         this.picker().addClass(cls.join('-'));
16626         
16627         var _this = this;
16628         
16629         Roo.each(cls, function(c){
16630             if(c == 'bottom'){
16631                 _this.picker().setTop(_this.inputEl().getHeight());
16632                 return;
16633             }
16634             if(c == 'top'){
16635                 _this.picker().setTop(0 - _this.picker().getHeight());
16636                 return;
16637             }
16638             
16639             if(c == 'left'){
16640                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16641                 return;
16642             }
16643             if(c == 'right'){
16644                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16645                 return;
16646             }
16647         });
16648         
16649     },
16650   
16651     onFocus : function()
16652     {
16653         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16654         this.show();
16655     },
16656     
16657     onBlur : function()
16658     {
16659         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16660         this.hide();
16661     },
16662     
16663     show : function()
16664     {
16665         this.picker().show();
16666         this.pop.show();
16667         this.update();
16668         this.place();
16669         
16670         this.fireEvent('show', this, this.date);
16671     },
16672     
16673     hide : function()
16674     {
16675         this.picker().hide();
16676         this.pop.hide();
16677         
16678         this.fireEvent('hide', this, this.date);
16679     },
16680     
16681     setTime : function()
16682     {
16683         this.hide();
16684         this.setValue(this.time.format(this.format));
16685         
16686         this.fireEvent('select', this, this.date);
16687         
16688         
16689     },
16690     
16691     onMousedown: function(e){
16692         e.stopPropagation();
16693         e.preventDefault();
16694     },
16695     
16696     onIncrementHours: function()
16697     {
16698         Roo.log('onIncrementHours');
16699         this.time = this.time.add(Date.HOUR, 1);
16700         this.update();
16701         
16702     },
16703     
16704     onDecrementHours: function()
16705     {
16706         Roo.log('onDecrementHours');
16707         this.time = this.time.add(Date.HOUR, -1);
16708         this.update();
16709     },
16710     
16711     onIncrementMinutes: function()
16712     {
16713         Roo.log('onIncrementMinutes');
16714         this.time = this.time.add(Date.MINUTE, 1);
16715         this.update();
16716     },
16717     
16718     onDecrementMinutes: function()
16719     {
16720         Roo.log('onDecrementMinutes');
16721         this.time = this.time.add(Date.MINUTE, -1);
16722         this.update();
16723     },
16724     
16725     onTogglePeriod: function()
16726     {
16727         Roo.log('onTogglePeriod');
16728         this.time = this.time.add(Date.HOUR, 12);
16729         this.update();
16730     }
16731     
16732    
16733 });
16734
16735 Roo.apply(Roo.bootstrap.TimeField,  {
16736     
16737     content : {
16738         tag: 'tbody',
16739         cn: [
16740             {
16741                 tag: 'tr',
16742                 cn: [
16743                 {
16744                     tag: 'td',
16745                     colspan: '7'
16746                 }
16747                 ]
16748             }
16749         ]
16750     },
16751     
16752     footer : {
16753         tag: 'tfoot',
16754         cn: [
16755             {
16756                 tag: 'tr',
16757                 cn: [
16758                 {
16759                     tag: 'th',
16760                     colspan: '7',
16761                     cls: '',
16762                     cn: [
16763                         {
16764                             tag: 'button',
16765                             cls: 'btn btn-info ok',
16766                             html: 'OK'
16767                         }
16768                     ]
16769                 }
16770
16771                 ]
16772             }
16773         ]
16774     }
16775 });
16776
16777 Roo.apply(Roo.bootstrap.TimeField,  {
16778   
16779     template : {
16780         tag: 'div',
16781         cls: 'datepicker dropdown-menu',
16782         cn: [
16783             {
16784                 tag: 'div',
16785                 cls: 'datepicker-time',
16786                 cn: [
16787                 {
16788                     tag: 'table',
16789                     cls: 'table-condensed',
16790                     cn:[
16791                     Roo.bootstrap.TimeField.content,
16792                     Roo.bootstrap.TimeField.footer
16793                     ]
16794                 }
16795                 ]
16796             }
16797         ]
16798     }
16799 });
16800
16801  
16802
16803  /*
16804  * - LGPL
16805  *
16806  * MonthField
16807  * 
16808  */
16809
16810 /**
16811  * @class Roo.bootstrap.MonthField
16812  * @extends Roo.bootstrap.Input
16813  * Bootstrap MonthField class
16814  * 
16815  * @cfg {String} language default en
16816  * 
16817  * @constructor
16818  * Create a new MonthField
16819  * @param {Object} config The config object
16820  */
16821
16822 Roo.bootstrap.MonthField = function(config){
16823     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16824     
16825     this.addEvents({
16826         /**
16827          * @event show
16828          * Fires when this field show.
16829          * @param {Roo.bootstrap.MonthField} this
16830          * @param {Mixed} date The date value
16831          */
16832         show : true,
16833         /**
16834          * @event show
16835          * Fires when this field hide.
16836          * @param {Roo.bootstrap.MonthField} this
16837          * @param {Mixed} date The date value
16838          */
16839         hide : true,
16840         /**
16841          * @event select
16842          * Fires when select a date.
16843          * @param {Roo.bootstrap.MonthField} this
16844          * @param {String} oldvalue The old value
16845          * @param {String} newvalue The new value
16846          */
16847         select : true
16848     });
16849 };
16850
16851 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16852     
16853     onRender: function(ct, position)
16854     {
16855         
16856         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16857         
16858         this.language = this.language || 'en';
16859         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16860         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16861         
16862         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16863         this.isInline = false;
16864         this.isInput = true;
16865         this.component = this.el.select('.add-on', true).first() || false;
16866         this.component = (this.component && this.component.length === 0) ? false : this.component;
16867         this.hasInput = this.component && this.inputEL().length;
16868         
16869         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16870         
16871         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16872         
16873         this.picker().on('mousedown', this.onMousedown, this);
16874         this.picker().on('click', this.onClick, this);
16875         
16876         this.picker().addClass('datepicker-dropdown');
16877         
16878         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16879             v.setStyle('width', '189px');
16880         });
16881         
16882         this.fillMonths();
16883         
16884         this.update();
16885         
16886         if(this.isInline) {
16887             this.show();
16888         }
16889         
16890     },
16891     
16892     setValue: function(v, suppressEvent)
16893     {   
16894         var o = this.getValue();
16895         
16896         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16897         
16898         this.update();
16899
16900         if(suppressEvent !== true){
16901             this.fireEvent('select', this, o, v);
16902         }
16903         
16904     },
16905     
16906     getValue: function()
16907     {
16908         return this.value;
16909     },
16910     
16911     onClick: function(e) 
16912     {
16913         e.stopPropagation();
16914         e.preventDefault();
16915         
16916         var target = e.getTarget();
16917         
16918         if(target.nodeName.toLowerCase() === 'i'){
16919             target = Roo.get(target).dom.parentNode;
16920         }
16921         
16922         var nodeName = target.nodeName;
16923         var className = target.className;
16924         var html = target.innerHTML;
16925         
16926         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16927             return;
16928         }
16929         
16930         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16931         
16932         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16933         
16934         this.hide();
16935                         
16936     },
16937     
16938     picker : function()
16939     {
16940         return this.pickerEl;
16941     },
16942     
16943     fillMonths: function()
16944     {    
16945         var i = 0;
16946         var months = this.picker().select('>.datepicker-months td', true).first();
16947         
16948         months.dom.innerHTML = '';
16949         
16950         while (i < 12) {
16951             var month = {
16952                 tag: 'span',
16953                 cls: 'month',
16954                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16955             }
16956             
16957             months.createChild(month);
16958         }
16959         
16960     },
16961     
16962     update: function()
16963     {
16964         var _this = this;
16965         
16966         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16967             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16968         }
16969         
16970         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16971             e.removeClass('active');
16972             
16973             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16974                 e.addClass('active');
16975             }
16976         })
16977     },
16978     
16979     place: function()
16980     {
16981         if(this.isInline) return;
16982         
16983         this.picker().removeClass(['bottom', 'top']);
16984         
16985         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16986             /*
16987              * place to the top of element!
16988              *
16989              */
16990             
16991             this.picker().addClass('top');
16992             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16993             
16994             return;
16995         }
16996         
16997         this.picker().addClass('bottom');
16998         
16999         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17000     },
17001     
17002     onFocus : function()
17003     {
17004         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17005         this.show();
17006     },
17007     
17008     onBlur : function()
17009     {
17010         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17011         
17012         var d = this.inputEl().getValue();
17013         
17014         this.setValue(d);
17015                 
17016         this.hide();
17017     },
17018     
17019     show : function()
17020     {
17021         this.picker().show();
17022         this.picker().select('>.datepicker-months', true).first().show();
17023         this.update();
17024         this.place();
17025         
17026         this.fireEvent('show', this, this.date);
17027     },
17028     
17029     hide : function()
17030     {
17031         if(this.isInline) return;
17032         this.picker().hide();
17033         this.fireEvent('hide', this, this.date);
17034         
17035     },
17036     
17037     onMousedown: function(e)
17038     {
17039         e.stopPropagation();
17040         e.preventDefault();
17041     },
17042     
17043     keyup: function(e)
17044     {
17045         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17046         this.update();
17047     },
17048
17049     fireKey: function(e)
17050     {
17051         if (!this.picker().isVisible()){
17052             if (e.keyCode == 27) // allow escape to hide and re-show picker
17053                 this.show();
17054             return;
17055         }
17056         
17057         var dir;
17058         
17059         switch(e.keyCode){
17060             case 27: // escape
17061                 this.hide();
17062                 e.preventDefault();
17063                 break;
17064             case 37: // left
17065             case 39: // right
17066                 dir = e.keyCode == 37 ? -1 : 1;
17067                 
17068                 this.vIndex = this.vIndex + dir;
17069                 
17070                 if(this.vIndex < 0){
17071                     this.vIndex = 0;
17072                 }
17073                 
17074                 if(this.vIndex > 11){
17075                     this.vIndex = 11;
17076                 }
17077                 
17078                 if(isNaN(this.vIndex)){
17079                     this.vIndex = 0;
17080                 }
17081                 
17082                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17083                 
17084                 break;
17085             case 38: // up
17086             case 40: // down
17087                 
17088                 dir = e.keyCode == 38 ? -1 : 1;
17089                 
17090                 this.vIndex = this.vIndex + dir * 4;
17091                 
17092                 if(this.vIndex < 0){
17093                     this.vIndex = 0;
17094                 }
17095                 
17096                 if(this.vIndex > 11){
17097                     this.vIndex = 11;
17098                 }
17099                 
17100                 if(isNaN(this.vIndex)){
17101                     this.vIndex = 0;
17102                 }
17103                 
17104                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17105                 break;
17106                 
17107             case 13: // enter
17108                 
17109                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17110                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17111                 }
17112                 
17113                 this.hide();
17114                 e.preventDefault();
17115                 break;
17116             case 9: // tab
17117                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17118                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17119                 }
17120                 this.hide();
17121                 break;
17122             case 16: // shift
17123             case 17: // ctrl
17124             case 18: // alt
17125                 break;
17126             default :
17127                 this.hide();
17128                 
17129         }
17130     },
17131     
17132     remove: function() 
17133     {
17134         this.picker().remove();
17135     }
17136    
17137 });
17138
17139 Roo.apply(Roo.bootstrap.MonthField,  {
17140     
17141     content : {
17142         tag: 'tbody',
17143         cn: [
17144         {
17145             tag: 'tr',
17146             cn: [
17147             {
17148                 tag: 'td',
17149                 colspan: '7'
17150             }
17151             ]
17152         }
17153         ]
17154     },
17155     
17156     dates:{
17157         en: {
17158             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17159             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17160         }
17161     }
17162 });
17163
17164 Roo.apply(Roo.bootstrap.MonthField,  {
17165   
17166     template : {
17167         tag: 'div',
17168         cls: 'datepicker dropdown-menu roo-dynamic',
17169         cn: [
17170             {
17171                 tag: 'div',
17172                 cls: 'datepicker-months',
17173                 cn: [
17174                 {
17175                     tag: 'table',
17176                     cls: 'table-condensed',
17177                     cn:[
17178                         Roo.bootstrap.DateField.content
17179                     ]
17180                 }
17181                 ]
17182             }
17183         ]
17184     }
17185 });
17186
17187  
17188
17189  
17190  /*
17191  * - LGPL
17192  *
17193  * CheckBox
17194  * 
17195  */
17196
17197 /**
17198  * @class Roo.bootstrap.CheckBox
17199  * @extends Roo.bootstrap.Input
17200  * Bootstrap CheckBox class
17201  * 
17202  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17203  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17204  * @cfg {String} boxLabel The text that appears beside the checkbox
17205  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17206  * @cfg {Boolean} checked initnal the element
17207  * @cfg {Boolean} inline inline the element (default false)
17208  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17209  * 
17210  * @constructor
17211  * Create a new CheckBox
17212  * @param {Object} config The config object
17213  */
17214
17215 Roo.bootstrap.CheckBox = function(config){
17216     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17217    
17218     this.addEvents({
17219         /**
17220         * @event check
17221         * Fires when the element is checked or unchecked.
17222         * @param {Roo.bootstrap.CheckBox} this This input
17223         * @param {Boolean} checked The new checked value
17224         */
17225        check : true
17226     });
17227     
17228 };
17229
17230 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17231   
17232     inputType: 'checkbox',
17233     inputValue: 1,
17234     valueOff: 0,
17235     boxLabel: false,
17236     checked: false,
17237     weight : false,
17238     inline: false,
17239     
17240     getAutoCreate : function()
17241     {
17242         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17243         
17244         var id = Roo.id();
17245         
17246         var cfg = {};
17247         
17248         cfg.cls = 'form-group ' + this.inputType; //input-group
17249         
17250         if(this.inline){
17251             cfg.cls += ' ' + this.inputType + '-inline';
17252         }
17253         
17254         var input =  {
17255             tag: 'input',
17256             id : id,
17257             type : this.inputType,
17258             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17259             cls : 'roo-' + this.inputType, //'form-box',
17260             placeholder : this.placeholder || ''
17261             
17262         };
17263         
17264         if (this.weight) { // Validity check?
17265             cfg.cls += " " + this.inputType + "-" + this.weight;
17266         }
17267         
17268         if (this.disabled) {
17269             input.disabled=true;
17270         }
17271         
17272         if(this.checked){
17273             input.checked = this.checked;
17274         }
17275         
17276         if (this.name) {
17277             input.name = this.name;
17278         }
17279         
17280         if (this.size) {
17281             input.cls += ' input-' + this.size;
17282         }
17283         
17284         var settings=this;
17285         
17286         ['xs','sm','md','lg'].map(function(size){
17287             if (settings[size]) {
17288                 cfg.cls += ' col-' + size + '-' + settings[size];
17289             }
17290         });
17291         
17292         var inputblock = input;
17293          
17294         if (this.before || this.after) {
17295             
17296             inputblock = {
17297                 cls : 'input-group',
17298                 cn :  [] 
17299             };
17300             
17301             if (this.before) {
17302                 inputblock.cn.push({
17303                     tag :'span',
17304                     cls : 'input-group-addon',
17305                     html : this.before
17306                 });
17307             }
17308             
17309             inputblock.cn.push(input);
17310             
17311             if (this.after) {
17312                 inputblock.cn.push({
17313                     tag :'span',
17314                     cls : 'input-group-addon',
17315                     html : this.after
17316                 });
17317             }
17318             
17319         }
17320         
17321         if (align ==='left' && this.fieldLabel.length) {
17322                 Roo.log("left and has label");
17323                 cfg.cn = [
17324                     
17325                     {
17326                         tag: 'label',
17327                         'for' :  id,
17328                         cls : 'control-label col-md-' + this.labelWidth,
17329                         html : this.fieldLabel
17330                         
17331                     },
17332                     {
17333                         cls : "col-md-" + (12 - this.labelWidth), 
17334                         cn: [
17335                             inputblock
17336                         ]
17337                     }
17338                     
17339                 ];
17340         } else if ( this.fieldLabel.length) {
17341                 Roo.log(" label");
17342                 cfg.cn = [
17343                    
17344                     {
17345                         tag: this.boxLabel ? 'span' : 'label',
17346                         'for': id,
17347                         cls: 'control-label box-input-label',
17348                         //cls : 'input-group-addon',
17349                         html : this.fieldLabel
17350                         
17351                     },
17352                     
17353                     inputblock
17354                     
17355                 ];
17356
17357         } else {
17358             
17359                 Roo.log(" no label && no align");
17360                 cfg.cn = [  inputblock ] ;
17361                 
17362                 
17363         }
17364         if(this.boxLabel){
17365              var boxLabelCfg = {
17366                 tag: 'label',
17367                 //'for': id, // box label is handled by onclick - so no for...
17368                 cls: 'box-label',
17369                 html: this.boxLabel
17370             }
17371             
17372             if(this.tooltip){
17373                 boxLabelCfg.tooltip = this.tooltip;
17374             }
17375              
17376             cfg.cn.push(boxLabelCfg);
17377         }
17378         
17379         
17380        
17381         return cfg;
17382         
17383     },
17384     
17385     /**
17386      * return the real input element.
17387      */
17388     inputEl: function ()
17389     {
17390         return this.el.select('input.roo-' + this.inputType,true).first();
17391     },
17392     
17393     labelEl: function()
17394     {
17395         return this.el.select('label.control-label',true).first();
17396     },
17397     /* depricated... */
17398     
17399     label: function()
17400     {
17401         return this.labelEl();
17402     },
17403     
17404     initEvents : function()
17405     {
17406 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17407         
17408         this.inputEl().on('click', this.onClick,  this);
17409         
17410         if (this.boxLabel) { 
17411             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17412         }
17413         
17414         this.startValue = this.getValue();
17415         
17416         if(this.groupId){
17417             Roo.bootstrap.CheckBox.register(this);
17418         }
17419     },
17420     
17421     onClick : function()
17422     {   
17423         this.setChecked(!this.checked);
17424     },
17425     
17426     setChecked : function(state,suppressEvent)
17427     {
17428         this.startValue = this.getValue();
17429         
17430         if(this.inputType == 'radio'){
17431             
17432             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17433                 e.dom.checked = false;
17434             });
17435             
17436             this.inputEl().dom.checked = true;
17437             
17438             this.inputEl().dom.value = this.inputValue;
17439             
17440             if(suppressEvent !== true){
17441                 this.fireEvent('check', this, true);
17442             }
17443             
17444             this.validate();
17445             
17446             return;
17447         }
17448         
17449         this.checked = state;
17450         
17451         this.inputEl().dom.checked = state;
17452         
17453         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17454         
17455         if(suppressEvent !== true){
17456             this.fireEvent('check', this, state);
17457         }
17458         
17459         this.validate();
17460     },
17461     
17462     getValue : function()
17463     {
17464         if(this.inputType == 'radio'){
17465             return this.getGroupValue();
17466         }
17467         
17468         return this.inputEl().getValue();
17469         
17470     },
17471     
17472     getGroupValue : function()
17473     {
17474         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17475             return '';
17476         }
17477         
17478         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17479     },
17480     
17481     setValue : function(v,suppressEvent)
17482     {
17483         if(this.inputType == 'radio'){
17484             this.setGroupValue(v, suppressEvent);
17485             return;
17486         }
17487         
17488         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17489         
17490         this.validate();
17491     },
17492     
17493     setGroupValue : function(v, suppressEvent)
17494     {
17495         this.startValue = this.getValue();
17496         
17497         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17498             e.dom.checked = false;
17499             
17500             if(e.dom.value == v){
17501                 e.dom.checked = true;
17502             }
17503         });
17504         
17505         if(suppressEvent !== true){
17506             this.fireEvent('check', this, true);
17507         }
17508
17509         this.validate();
17510         
17511         return;
17512     },
17513     
17514     validate : function()
17515     {
17516         if(
17517                 this.disabled || 
17518                 (this.inputType == 'radio' && this.validateRadio()) ||
17519                 (this.inputType == 'checkbox' && this.validateCheckbox())
17520         ){
17521             this.markValid();
17522             return true;
17523         }
17524         
17525         this.markInvalid();
17526         return false;
17527     },
17528     
17529     validateRadio : function()
17530     {
17531         var valid = false;
17532         
17533         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17534             if(!e.dom.checked){
17535                 return;
17536             }
17537             
17538             valid = true;
17539             
17540             return false;
17541         });
17542         
17543         return valid;
17544     },
17545     
17546     validateCheckbox : function()
17547     {
17548         if(!this.groupId){
17549             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17550         }
17551         
17552         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17553         
17554         if(!group){
17555             return false;
17556         }
17557         
17558         var r = false;
17559         
17560         for(var i in group){
17561             if(r){
17562                 break;
17563             }
17564             
17565             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17566         }
17567         
17568         return r;
17569     },
17570     
17571     /**
17572      * Mark this field as valid
17573      */
17574     markValid : function()
17575     {
17576         if(this.allowBlank){
17577             return;
17578         }
17579         
17580         var _this = this;
17581         
17582         this.fireEvent('valid', this);
17583         
17584         if(this.inputType == 'radio'){
17585             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17586                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17587                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17588             });
17589             
17590             return;
17591         }
17592         
17593         if(!this.groupId){
17594             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17595             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17596             return;
17597         }
17598         
17599         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17600             
17601         if(!group){
17602             return;
17603         }
17604         
17605         for(var i in group){
17606             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17607             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17608         }
17609     },
17610     
17611      /**
17612      * Mark this field as invalid
17613      * @param {String} msg The validation message
17614      */
17615     markInvalid : function(msg)
17616     {
17617         if(this.allowBlank){
17618             return;
17619         }
17620         
17621         var _this = this;
17622         
17623         this.fireEvent('invalid', this, msg);
17624         
17625         if(this.inputType == 'radio'){
17626             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17627                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17628                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17629             });
17630             
17631             return;
17632         }
17633         
17634         if(!this.groupId){
17635             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17636             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17637             return;
17638         }
17639         
17640         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17641             
17642         if(!group){
17643             return;
17644         }
17645         
17646         for(var i in group){
17647             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17648             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17649         }
17650         
17651     }
17652     
17653 });
17654
17655 Roo.apply(Roo.bootstrap.CheckBox, {
17656     
17657     groups: {},
17658     
17659      /**
17660     * register a CheckBox Group
17661     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17662     */
17663     register : function(checkbox)
17664     {
17665         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17666             this.groups[checkbox.groupId] = {};
17667         }
17668         
17669         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17670             return;
17671         }
17672         
17673         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17674         
17675     },
17676     /**
17677     * fetch a CheckBox Group based on the group ID
17678     * @param {string} the group ID
17679     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17680     */
17681     get: function(groupId) {
17682         if (typeof(this.groups[groupId]) == 'undefined') {
17683             return false;
17684         }
17685         
17686         return this.groups[groupId] ;
17687     }
17688     
17689     
17690 });
17691 /*
17692  * - LGPL
17693  *
17694  * Radio
17695  *
17696  *
17697  * not inline
17698  *<div class="radio">
17699   <label>
17700     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17701     Option one is this and that&mdash;be sure to include why it's great
17702   </label>
17703 </div>
17704  *
17705  *
17706  *inline
17707  *<span>
17708  *<label class="radio-inline">fieldLabel</label>
17709  *<label class="radio-inline">
17710   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17711 </label>
17712 <span>
17713  * 
17714  * 
17715  */
17716
17717 /**
17718  * @class Roo.bootstrap.Radio
17719  * @extends Roo.bootstrap.CheckBox
17720  * Bootstrap Radio class
17721
17722  * @constructor
17723  * Create a new Radio
17724  * @param {Object} config The config object
17725  */
17726
17727 Roo.bootstrap.Radio = function(config){
17728     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17729    
17730 };
17731
17732 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17733     
17734     inputType: 'radio',
17735     inputValue: '',
17736     valueOff: '',
17737     
17738     getAutoCreate : function()
17739     {
17740         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17741         align = align || 'left'; // default...
17742         
17743         
17744         
17745         var id = Roo.id();
17746         
17747         var cfg = {
17748                 tag : this.inline ? 'span' : 'div',
17749                 cls : '',
17750                 cn : []
17751         };
17752         
17753         var inline = this.inline ? ' radio-inline' : '';
17754         
17755         var lbl = {
17756                 tag: 'label' ,
17757                 // does not need for, as we wrap the input with it..
17758                 'for' : id,
17759                 cls : 'control-label box-label' + inline,
17760                 cn : []
17761         };
17762         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17763         
17764         var fieldLabel = {
17765             tag: 'label' ,
17766             //cls : 'control-label' + inline,
17767             html : this.fieldLabel,
17768             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17769         };
17770         
17771  
17772         
17773         
17774         var input =  {
17775             tag: 'input',
17776             id : id,
17777             type : this.inputType,
17778             //value : (!this.checked) ? this.valueOff : this.inputValue,
17779             value : this.inputValue,
17780             cls : 'roo-radio',
17781             placeholder : this.placeholder || '' // ?? needed????
17782             
17783         };
17784         if (this.weight) { // Validity check?
17785             input.cls += " radio-" + this.weight;
17786         }
17787         if (this.disabled) {
17788             input.disabled=true;
17789         }
17790         
17791         if(this.checked){
17792             input.checked = this.checked;
17793         }
17794         
17795         if (this.name) {
17796             input.name = this.name;
17797         }
17798         
17799         if (this.size) {
17800             input.cls += ' input-' + this.size;
17801         }
17802         
17803         //?? can span's inline have a width??
17804         
17805         var settings=this;
17806         ['xs','sm','md','lg'].map(function(size){
17807             if (settings[size]) {
17808                 cfg.cls += ' col-' + size + '-' + settings[size];
17809             }
17810         });
17811         
17812         var inputblock = input;
17813         
17814         if (this.before || this.after) {
17815             
17816             inputblock = {
17817                 cls : 'input-group',
17818                 tag : 'span',
17819                 cn :  [] 
17820             };
17821             if (this.before) {
17822                 inputblock.cn.push({
17823                     tag :'span',
17824                     cls : 'input-group-addon',
17825                     html : this.before
17826                 });
17827             }
17828             inputblock.cn.push(input);
17829             if (this.after) {
17830                 inputblock.cn.push({
17831                     tag :'span',
17832                     cls : 'input-group-addon',
17833                     html : this.after
17834                 });
17835             }
17836             
17837         };
17838         
17839         
17840         if (this.fieldLabel && this.fieldLabel.length) {
17841             cfg.cn.push(fieldLabel);
17842         }
17843        
17844         // normal bootstrap puts the input inside the label.
17845         // however with our styled version - it has to go after the input.
17846        
17847         //lbl.cn.push(inputblock);
17848         
17849         var lblwrap =  {
17850             tag: 'span',
17851             cls: 'radio' + inline,
17852             cn: [
17853                 inputblock,
17854                 lbl
17855             ]
17856         };
17857         
17858         cfg.cn.push( lblwrap);
17859         
17860         if(this.boxLabel){
17861             lbl.cn.push({
17862                 tag: 'span',
17863                 html: this.boxLabel
17864             })
17865         }
17866          
17867         
17868         return cfg;
17869         
17870     },
17871     
17872     initEvents : function()
17873     {
17874 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17875         
17876         this.inputEl().on('click', this.onClick,  this);
17877         if (this.boxLabel) {
17878             Roo.log('find label')
17879             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17880         }
17881         
17882     },
17883     
17884     inputEl: function ()
17885     {
17886         return this.el.select('input.roo-radio',true).first();
17887     },
17888     onClick : function()
17889     {   
17890         Roo.log("click");
17891         this.setChecked(true);
17892     },
17893     
17894     setChecked : function(state,suppressEvent)
17895     {
17896         if(state){
17897             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17898                 v.dom.checked = false;
17899             });
17900         }
17901         Roo.log(this.inputEl().dom);
17902         this.checked = state;
17903         this.inputEl().dom.checked = state;
17904         
17905         if(suppressEvent !== true){
17906             this.fireEvent('check', this, state);
17907         }
17908         
17909         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17910         
17911     },
17912     
17913     getGroupValue : function()
17914     {
17915         var value = '';
17916         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17917             if(v.dom.checked == true){
17918                 value = v.dom.value;
17919             }
17920         });
17921         
17922         return value;
17923     },
17924     
17925     /**
17926      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17927      * @return {Mixed} value The field value
17928      */
17929     getValue : function(){
17930         return this.getGroupValue();
17931     }
17932     
17933 });
17934
17935  
17936 //<script type="text/javascript">
17937
17938 /*
17939  * Based  Ext JS Library 1.1.1
17940  * Copyright(c) 2006-2007, Ext JS, LLC.
17941  * LGPL
17942  *
17943  */
17944  
17945 /**
17946  * @class Roo.HtmlEditorCore
17947  * @extends Roo.Component
17948  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17949  *
17950  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17951  */
17952
17953 Roo.HtmlEditorCore = function(config){
17954     
17955     
17956     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17957     
17958     
17959     this.addEvents({
17960         /**
17961          * @event initialize
17962          * Fires when the editor is fully initialized (including the iframe)
17963          * @param {Roo.HtmlEditorCore} this
17964          */
17965         initialize: true,
17966         /**
17967          * @event activate
17968          * Fires when the editor is first receives the focus. Any insertion must wait
17969          * until after this event.
17970          * @param {Roo.HtmlEditorCore} this
17971          */
17972         activate: true,
17973          /**
17974          * @event beforesync
17975          * Fires before the textarea is updated with content from the editor iframe. Return false
17976          * to cancel the sync.
17977          * @param {Roo.HtmlEditorCore} this
17978          * @param {String} html
17979          */
17980         beforesync: true,
17981          /**
17982          * @event beforepush
17983          * Fires before the iframe editor is updated with content from the textarea. Return false
17984          * to cancel the push.
17985          * @param {Roo.HtmlEditorCore} this
17986          * @param {String} html
17987          */
17988         beforepush: true,
17989          /**
17990          * @event sync
17991          * Fires when the textarea is updated with content from the editor iframe.
17992          * @param {Roo.HtmlEditorCore} this
17993          * @param {String} html
17994          */
17995         sync: true,
17996          /**
17997          * @event push
17998          * Fires when the iframe editor is updated with content from the textarea.
17999          * @param {Roo.HtmlEditorCore} this
18000          * @param {String} html
18001          */
18002         push: true,
18003         
18004         /**
18005          * @event editorevent
18006          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18007          * @param {Roo.HtmlEditorCore} this
18008          */
18009         editorevent: true
18010         
18011     });
18012     
18013     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18014     
18015     // defaults : white / black...
18016     this.applyBlacklists();
18017     
18018     
18019     
18020 };
18021
18022
18023 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18024
18025
18026      /**
18027      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18028      */
18029     
18030     owner : false,
18031     
18032      /**
18033      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18034      *                        Roo.resizable.
18035      */
18036     resizable : false,
18037      /**
18038      * @cfg {Number} height (in pixels)
18039      */   
18040     height: 300,
18041    /**
18042      * @cfg {Number} width (in pixels)
18043      */   
18044     width: 500,
18045     
18046     /**
18047      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18048      * 
18049      */
18050     stylesheets: false,
18051     
18052     // id of frame..
18053     frameId: false,
18054     
18055     // private properties
18056     validationEvent : false,
18057     deferHeight: true,
18058     initialized : false,
18059     activated : false,
18060     sourceEditMode : false,
18061     onFocus : Roo.emptyFn,
18062     iframePad:3,
18063     hideMode:'offsets',
18064     
18065     clearUp: true,
18066     
18067     // blacklist + whitelisted elements..
18068     black: false,
18069     white: false,
18070      
18071     
18072
18073     /**
18074      * Protected method that will not generally be called directly. It
18075      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18076      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18077      */
18078     getDocMarkup : function(){
18079         // body styles..
18080         var st = '';
18081         
18082         // inherit styels from page...?? 
18083         if (this.stylesheets === false) {
18084             
18085             Roo.get(document.head).select('style').each(function(node) {
18086                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18087             });
18088             
18089             Roo.get(document.head).select('link').each(function(node) { 
18090                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18091             });
18092             
18093         } else if (!this.stylesheets.length) {
18094                 // simple..
18095                 st = '<style type="text/css">' +
18096                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18097                    '</style>';
18098         } else { 
18099             
18100         }
18101         
18102         st +=  '<style type="text/css">' +
18103             'IMG { cursor: pointer } ' +
18104         '</style>';
18105
18106         
18107         return '<html><head>' + st  +
18108             //<style type="text/css">' +
18109             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18110             //'</style>' +
18111             ' </head><body class="roo-htmleditor-body"></body></html>';
18112     },
18113
18114     // private
18115     onRender : function(ct, position)
18116     {
18117         var _t = this;
18118         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18119         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18120         
18121         
18122         this.el.dom.style.border = '0 none';
18123         this.el.dom.setAttribute('tabIndex', -1);
18124         this.el.addClass('x-hidden hide');
18125         
18126         
18127         
18128         if(Roo.isIE){ // fix IE 1px bogus margin
18129             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18130         }
18131        
18132         
18133         this.frameId = Roo.id();
18134         
18135          
18136         
18137         var iframe = this.owner.wrap.createChild({
18138             tag: 'iframe',
18139             cls: 'form-control', // bootstrap..
18140             id: this.frameId,
18141             name: this.frameId,
18142             frameBorder : 'no',
18143             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18144         }, this.el
18145         );
18146         
18147         
18148         this.iframe = iframe.dom;
18149
18150          this.assignDocWin();
18151         
18152         this.doc.designMode = 'on';
18153        
18154         this.doc.open();
18155         this.doc.write(this.getDocMarkup());
18156         this.doc.close();
18157
18158         
18159         var task = { // must defer to wait for browser to be ready
18160             run : function(){
18161                 //console.log("run task?" + this.doc.readyState);
18162                 this.assignDocWin();
18163                 if(this.doc.body || this.doc.readyState == 'complete'){
18164                     try {
18165                         this.doc.designMode="on";
18166                     } catch (e) {
18167                         return;
18168                     }
18169                     Roo.TaskMgr.stop(task);
18170                     this.initEditor.defer(10, this);
18171                 }
18172             },
18173             interval : 10,
18174             duration: 10000,
18175             scope: this
18176         };
18177         Roo.TaskMgr.start(task);
18178
18179     },
18180
18181     // private
18182     onResize : function(w, h)
18183     {
18184          Roo.log('resize: ' +w + ',' + h );
18185         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18186         if(!this.iframe){
18187             return;
18188         }
18189         if(typeof w == 'number'){
18190             
18191             this.iframe.style.width = w + 'px';
18192         }
18193         if(typeof h == 'number'){
18194             
18195             this.iframe.style.height = h + 'px';
18196             if(this.doc){
18197                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18198             }
18199         }
18200         
18201     },
18202
18203     /**
18204      * Toggles the editor between standard and source edit mode.
18205      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18206      */
18207     toggleSourceEdit : function(sourceEditMode){
18208         
18209         this.sourceEditMode = sourceEditMode === true;
18210         
18211         if(this.sourceEditMode){
18212  
18213             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18214             
18215         }else{
18216             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18217             //this.iframe.className = '';
18218             this.deferFocus();
18219         }
18220         //this.setSize(this.owner.wrap.getSize());
18221         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18222     },
18223
18224     
18225   
18226
18227     /**
18228      * Protected method that will not generally be called directly. If you need/want
18229      * custom HTML cleanup, this is the method you should override.
18230      * @param {String} html The HTML to be cleaned
18231      * return {String} The cleaned HTML
18232      */
18233     cleanHtml : function(html){
18234         html = String(html);
18235         if(html.length > 5){
18236             if(Roo.isSafari){ // strip safari nonsense
18237                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18238             }
18239         }
18240         if(html == '&nbsp;'){
18241             html = '';
18242         }
18243         return html;
18244     },
18245
18246     /**
18247      * HTML Editor -> Textarea
18248      * Protected method that will not generally be called directly. Syncs the contents
18249      * of the editor iframe with the textarea.
18250      */
18251     syncValue : function(){
18252         if(this.initialized){
18253             var bd = (this.doc.body || this.doc.documentElement);
18254             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18255             var html = bd.innerHTML;
18256             if(Roo.isSafari){
18257                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18258                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18259                 if(m && m[1]){
18260                     html = '<div style="'+m[0]+'">' + html + '</div>';
18261                 }
18262             }
18263             html = this.cleanHtml(html);
18264             // fix up the special chars.. normaly like back quotes in word...
18265             // however we do not want to do this with chinese..
18266             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18267                 var cc = b.charCodeAt();
18268                 if (
18269                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18270                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18271                     (cc >= 0xf900 && cc < 0xfb00 )
18272                 ) {
18273                         return b;
18274                 }
18275                 return "&#"+cc+";" 
18276             });
18277             if(this.owner.fireEvent('beforesync', this, html) !== false){
18278                 this.el.dom.value = html;
18279                 this.owner.fireEvent('sync', this, html);
18280             }
18281         }
18282     },
18283
18284     /**
18285      * Protected method that will not generally be called directly. Pushes the value of the textarea
18286      * into the iframe editor.
18287      */
18288     pushValue : function(){
18289         if(this.initialized){
18290             var v = this.el.dom.value.trim();
18291             
18292 //            if(v.length < 1){
18293 //                v = '&#160;';
18294 //            }
18295             
18296             if(this.owner.fireEvent('beforepush', this, v) !== false){
18297                 var d = (this.doc.body || this.doc.documentElement);
18298                 d.innerHTML = v;
18299                 this.cleanUpPaste();
18300                 this.el.dom.value = d.innerHTML;
18301                 this.owner.fireEvent('push', this, v);
18302             }
18303         }
18304     },
18305
18306     // private
18307     deferFocus : function(){
18308         this.focus.defer(10, this);
18309     },
18310
18311     // doc'ed in Field
18312     focus : function(){
18313         if(this.win && !this.sourceEditMode){
18314             this.win.focus();
18315         }else{
18316             this.el.focus();
18317         }
18318     },
18319     
18320     assignDocWin: function()
18321     {
18322         var iframe = this.iframe;
18323         
18324          if(Roo.isIE){
18325             this.doc = iframe.contentWindow.document;
18326             this.win = iframe.contentWindow;
18327         } else {
18328 //            if (!Roo.get(this.frameId)) {
18329 //                return;
18330 //            }
18331 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18332 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18333             
18334             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18335                 return;
18336             }
18337             
18338             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18339             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18340         }
18341     },
18342     
18343     // private
18344     initEditor : function(){
18345         //console.log("INIT EDITOR");
18346         this.assignDocWin();
18347         
18348         
18349         
18350         this.doc.designMode="on";
18351         this.doc.open();
18352         this.doc.write(this.getDocMarkup());
18353         this.doc.close();
18354         
18355         var dbody = (this.doc.body || this.doc.documentElement);
18356         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18357         // this copies styles from the containing element into thsi one..
18358         // not sure why we need all of this..
18359         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18360         
18361         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18362         //ss['background-attachment'] = 'fixed'; // w3c
18363         dbody.bgProperties = 'fixed'; // ie
18364         //Roo.DomHelper.applyStyles(dbody, ss);
18365         Roo.EventManager.on(this.doc, {
18366             //'mousedown': this.onEditorEvent,
18367             'mouseup': this.onEditorEvent,
18368             'dblclick': this.onEditorEvent,
18369             'click': this.onEditorEvent,
18370             'keyup': this.onEditorEvent,
18371             buffer:100,
18372             scope: this
18373         });
18374         if(Roo.isGecko){
18375             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18376         }
18377         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18378             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18379         }
18380         this.initialized = true;
18381
18382         this.owner.fireEvent('initialize', this);
18383         this.pushValue();
18384     },
18385
18386     // private
18387     onDestroy : function(){
18388         
18389         
18390         
18391         if(this.rendered){
18392             
18393             //for (var i =0; i < this.toolbars.length;i++) {
18394             //    // fixme - ask toolbars for heights?
18395             //    this.toolbars[i].onDestroy();
18396            // }
18397             
18398             //this.wrap.dom.innerHTML = '';
18399             //this.wrap.remove();
18400         }
18401     },
18402
18403     // private
18404     onFirstFocus : function(){
18405         
18406         this.assignDocWin();
18407         
18408         
18409         this.activated = true;
18410          
18411     
18412         if(Roo.isGecko){ // prevent silly gecko errors
18413             this.win.focus();
18414             var s = this.win.getSelection();
18415             if(!s.focusNode || s.focusNode.nodeType != 3){
18416                 var r = s.getRangeAt(0);
18417                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18418                 r.collapse(true);
18419                 this.deferFocus();
18420             }
18421             try{
18422                 this.execCmd('useCSS', true);
18423                 this.execCmd('styleWithCSS', false);
18424             }catch(e){}
18425         }
18426         this.owner.fireEvent('activate', this);
18427     },
18428
18429     // private
18430     adjustFont: function(btn){
18431         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18432         //if(Roo.isSafari){ // safari
18433         //    adjust *= 2;
18434        // }
18435         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18436         if(Roo.isSafari){ // safari
18437             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18438             v =  (v < 10) ? 10 : v;
18439             v =  (v > 48) ? 48 : v;
18440             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18441             
18442         }
18443         
18444         
18445         v = Math.max(1, v+adjust);
18446         
18447         this.execCmd('FontSize', v  );
18448     },
18449
18450     onEditorEvent : function(e){
18451         this.owner.fireEvent('editorevent', this, e);
18452       //  this.updateToolbar();
18453         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18454     },
18455
18456     insertTag : function(tg)
18457     {
18458         // could be a bit smarter... -> wrap the current selected tRoo..
18459         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18460             
18461             range = this.createRange(this.getSelection());
18462             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18463             wrappingNode.appendChild(range.extractContents());
18464             range.insertNode(wrappingNode);
18465
18466             return;
18467             
18468             
18469             
18470         }
18471         this.execCmd("formatblock",   tg);
18472         
18473     },
18474     
18475     insertText : function(txt)
18476     {
18477         
18478         
18479         var range = this.createRange();
18480         range.deleteContents();
18481                //alert(Sender.getAttribute('label'));
18482                
18483         range.insertNode(this.doc.createTextNode(txt));
18484     } ,
18485     
18486      
18487
18488     /**
18489      * Executes a Midas editor command on the editor document and performs necessary focus and
18490      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18491      * @param {String} cmd The Midas command
18492      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18493      */
18494     relayCmd : function(cmd, value){
18495         this.win.focus();
18496         this.execCmd(cmd, value);
18497         this.owner.fireEvent('editorevent', this);
18498         //this.updateToolbar();
18499         this.owner.deferFocus();
18500     },
18501
18502     /**
18503      * Executes a Midas editor command directly on the editor document.
18504      * For visual commands, you should use {@link #relayCmd} instead.
18505      * <b>This should only be called after the editor is initialized.</b>
18506      * @param {String} cmd The Midas command
18507      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18508      */
18509     execCmd : function(cmd, value){
18510         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18511         this.syncValue();
18512     },
18513  
18514  
18515    
18516     /**
18517      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18518      * to insert tRoo.
18519      * @param {String} text | dom node.. 
18520      */
18521     insertAtCursor : function(text)
18522     {
18523         
18524         
18525         
18526         if(!this.activated){
18527             return;
18528         }
18529         /*
18530         if(Roo.isIE){
18531             this.win.focus();
18532             var r = this.doc.selection.createRange();
18533             if(r){
18534                 r.collapse(true);
18535                 r.pasteHTML(text);
18536                 this.syncValue();
18537                 this.deferFocus();
18538             
18539             }
18540             return;
18541         }
18542         */
18543         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18544             this.win.focus();
18545             
18546             
18547             // from jquery ui (MIT licenced)
18548             var range, node;
18549             var win = this.win;
18550             
18551             if (win.getSelection && win.getSelection().getRangeAt) {
18552                 range = win.getSelection().getRangeAt(0);
18553                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18554                 range.insertNode(node);
18555             } else if (win.document.selection && win.document.selection.createRange) {
18556                 // no firefox support
18557                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18558                 win.document.selection.createRange().pasteHTML(txt);
18559             } else {
18560                 // no firefox support
18561                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18562                 this.execCmd('InsertHTML', txt);
18563             } 
18564             
18565             this.syncValue();
18566             
18567             this.deferFocus();
18568         }
18569     },
18570  // private
18571     mozKeyPress : function(e){
18572         if(e.ctrlKey){
18573             var c = e.getCharCode(), cmd;
18574           
18575             if(c > 0){
18576                 c = String.fromCharCode(c).toLowerCase();
18577                 switch(c){
18578                     case 'b':
18579                         cmd = 'bold';
18580                         break;
18581                     case 'i':
18582                         cmd = 'italic';
18583                         break;
18584                     
18585                     case 'u':
18586                         cmd = 'underline';
18587                         break;
18588                     
18589                     case 'v':
18590                         this.cleanUpPaste.defer(100, this);
18591                         return;
18592                         
18593                 }
18594                 if(cmd){
18595                     this.win.focus();
18596                     this.execCmd(cmd);
18597                     this.deferFocus();
18598                     e.preventDefault();
18599                 }
18600                 
18601             }
18602         }
18603     },
18604
18605     // private
18606     fixKeys : function(){ // load time branching for fastest keydown performance
18607         if(Roo.isIE){
18608             return function(e){
18609                 var k = e.getKey(), r;
18610                 if(k == e.TAB){
18611                     e.stopEvent();
18612                     r = this.doc.selection.createRange();
18613                     if(r){
18614                         r.collapse(true);
18615                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18616                         this.deferFocus();
18617                     }
18618                     return;
18619                 }
18620                 
18621                 if(k == e.ENTER){
18622                     r = this.doc.selection.createRange();
18623                     if(r){
18624                         var target = r.parentElement();
18625                         if(!target || target.tagName.toLowerCase() != 'li'){
18626                             e.stopEvent();
18627                             r.pasteHTML('<br />');
18628                             r.collapse(false);
18629                             r.select();
18630                         }
18631                     }
18632                 }
18633                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18634                     this.cleanUpPaste.defer(100, this);
18635                     return;
18636                 }
18637                 
18638                 
18639             };
18640         }else if(Roo.isOpera){
18641             return function(e){
18642                 var k = e.getKey();
18643                 if(k == e.TAB){
18644                     e.stopEvent();
18645                     this.win.focus();
18646                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18647                     this.deferFocus();
18648                 }
18649                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18650                     this.cleanUpPaste.defer(100, this);
18651                     return;
18652                 }
18653                 
18654             };
18655         }else if(Roo.isSafari){
18656             return function(e){
18657                 var k = e.getKey();
18658                 
18659                 if(k == e.TAB){
18660                     e.stopEvent();
18661                     this.execCmd('InsertText','\t');
18662                     this.deferFocus();
18663                     return;
18664                 }
18665                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18666                     this.cleanUpPaste.defer(100, this);
18667                     return;
18668                 }
18669                 
18670              };
18671         }
18672     }(),
18673     
18674     getAllAncestors: function()
18675     {
18676         var p = this.getSelectedNode();
18677         var a = [];
18678         if (!p) {
18679             a.push(p); // push blank onto stack..
18680             p = this.getParentElement();
18681         }
18682         
18683         
18684         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18685             a.push(p);
18686             p = p.parentNode;
18687         }
18688         a.push(this.doc.body);
18689         return a;
18690     },
18691     lastSel : false,
18692     lastSelNode : false,
18693     
18694     
18695     getSelection : function() 
18696     {
18697         this.assignDocWin();
18698         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18699     },
18700     
18701     getSelectedNode: function() 
18702     {
18703         // this may only work on Gecko!!!
18704         
18705         // should we cache this!!!!
18706         
18707         
18708         
18709          
18710         var range = this.createRange(this.getSelection()).cloneRange();
18711         
18712         if (Roo.isIE) {
18713             var parent = range.parentElement();
18714             while (true) {
18715                 var testRange = range.duplicate();
18716                 testRange.moveToElementText(parent);
18717                 if (testRange.inRange(range)) {
18718                     break;
18719                 }
18720                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18721                     break;
18722                 }
18723                 parent = parent.parentElement;
18724             }
18725             return parent;
18726         }
18727         
18728         // is ancestor a text element.
18729         var ac =  range.commonAncestorContainer;
18730         if (ac.nodeType == 3) {
18731             ac = ac.parentNode;
18732         }
18733         
18734         var ar = ac.childNodes;
18735          
18736         var nodes = [];
18737         var other_nodes = [];
18738         var has_other_nodes = false;
18739         for (var i=0;i<ar.length;i++) {
18740             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18741                 continue;
18742             }
18743             // fullly contained node.
18744             
18745             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18746                 nodes.push(ar[i]);
18747                 continue;
18748             }
18749             
18750             // probably selected..
18751             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18752                 other_nodes.push(ar[i]);
18753                 continue;
18754             }
18755             // outer..
18756             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18757                 continue;
18758             }
18759             
18760             
18761             has_other_nodes = true;
18762         }
18763         if (!nodes.length && other_nodes.length) {
18764             nodes= other_nodes;
18765         }
18766         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18767             return false;
18768         }
18769         
18770         return nodes[0];
18771     },
18772     createRange: function(sel)
18773     {
18774         // this has strange effects when using with 
18775         // top toolbar - not sure if it's a great idea.
18776         //this.editor.contentWindow.focus();
18777         if (typeof sel != "undefined") {
18778             try {
18779                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18780             } catch(e) {
18781                 return this.doc.createRange();
18782             }
18783         } else {
18784             return this.doc.createRange();
18785         }
18786     },
18787     getParentElement: function()
18788     {
18789         
18790         this.assignDocWin();
18791         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18792         
18793         var range = this.createRange(sel);
18794          
18795         try {
18796             var p = range.commonAncestorContainer;
18797             while (p.nodeType == 3) { // text node
18798                 p = p.parentNode;
18799             }
18800             return p;
18801         } catch (e) {
18802             return null;
18803         }
18804     
18805     },
18806     /***
18807      *
18808      * Range intersection.. the hard stuff...
18809      *  '-1' = before
18810      *  '0' = hits..
18811      *  '1' = after.
18812      *         [ -- selected range --- ]
18813      *   [fail]                        [fail]
18814      *
18815      *    basically..
18816      *      if end is before start or  hits it. fail.
18817      *      if start is after end or hits it fail.
18818      *
18819      *   if either hits (but other is outside. - then it's not 
18820      *   
18821      *    
18822      **/
18823     
18824     
18825     // @see http://www.thismuchiknow.co.uk/?p=64.
18826     rangeIntersectsNode : function(range, node)
18827     {
18828         var nodeRange = node.ownerDocument.createRange();
18829         try {
18830             nodeRange.selectNode(node);
18831         } catch (e) {
18832             nodeRange.selectNodeContents(node);
18833         }
18834     
18835         var rangeStartRange = range.cloneRange();
18836         rangeStartRange.collapse(true);
18837     
18838         var rangeEndRange = range.cloneRange();
18839         rangeEndRange.collapse(false);
18840     
18841         var nodeStartRange = nodeRange.cloneRange();
18842         nodeStartRange.collapse(true);
18843     
18844         var nodeEndRange = nodeRange.cloneRange();
18845         nodeEndRange.collapse(false);
18846     
18847         return rangeStartRange.compareBoundaryPoints(
18848                  Range.START_TO_START, nodeEndRange) == -1 &&
18849                rangeEndRange.compareBoundaryPoints(
18850                  Range.START_TO_START, nodeStartRange) == 1;
18851         
18852          
18853     },
18854     rangeCompareNode : function(range, node)
18855     {
18856         var nodeRange = node.ownerDocument.createRange();
18857         try {
18858             nodeRange.selectNode(node);
18859         } catch (e) {
18860             nodeRange.selectNodeContents(node);
18861         }
18862         
18863         
18864         range.collapse(true);
18865     
18866         nodeRange.collapse(true);
18867      
18868         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18869         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18870          
18871         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18872         
18873         var nodeIsBefore   =  ss == 1;
18874         var nodeIsAfter    = ee == -1;
18875         
18876         if (nodeIsBefore && nodeIsAfter)
18877             return 0; // outer
18878         if (!nodeIsBefore && nodeIsAfter)
18879             return 1; //right trailed.
18880         
18881         if (nodeIsBefore && !nodeIsAfter)
18882             return 2;  // left trailed.
18883         // fully contined.
18884         return 3;
18885     },
18886
18887     // private? - in a new class?
18888     cleanUpPaste :  function()
18889     {
18890         // cleans up the whole document..
18891         Roo.log('cleanuppaste');
18892         
18893         this.cleanUpChildren(this.doc.body);
18894         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18895         if (clean != this.doc.body.innerHTML) {
18896             this.doc.body.innerHTML = clean;
18897         }
18898         
18899     },
18900     
18901     cleanWordChars : function(input) {// change the chars to hex code
18902         var he = Roo.HtmlEditorCore;
18903         
18904         var output = input;
18905         Roo.each(he.swapCodes, function(sw) { 
18906             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18907             
18908             output = output.replace(swapper, sw[1]);
18909         });
18910         
18911         return output;
18912     },
18913     
18914     
18915     cleanUpChildren : function (n)
18916     {
18917         if (!n.childNodes.length) {
18918             return;
18919         }
18920         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18921            this.cleanUpChild(n.childNodes[i]);
18922         }
18923     },
18924     
18925     
18926         
18927     
18928     cleanUpChild : function (node)
18929     {
18930         var ed = this;
18931         //console.log(node);
18932         if (node.nodeName == "#text") {
18933             // clean up silly Windows -- stuff?
18934             return; 
18935         }
18936         if (node.nodeName == "#comment") {
18937             node.parentNode.removeChild(node);
18938             // clean up silly Windows -- stuff?
18939             return; 
18940         }
18941         var lcname = node.tagName.toLowerCase();
18942         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18943         // whitelist of tags..
18944         
18945         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18946             // remove node.
18947             node.parentNode.removeChild(node);
18948             return;
18949             
18950         }
18951         
18952         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18953         
18954         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18955         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18956         
18957         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18958         //    remove_keep_children = true;
18959         //}
18960         
18961         if (remove_keep_children) {
18962             this.cleanUpChildren(node);
18963             // inserts everything just before this node...
18964             while (node.childNodes.length) {
18965                 var cn = node.childNodes[0];
18966                 node.removeChild(cn);
18967                 node.parentNode.insertBefore(cn, node);
18968             }
18969             node.parentNode.removeChild(node);
18970             return;
18971         }
18972         
18973         if (!node.attributes || !node.attributes.length) {
18974             this.cleanUpChildren(node);
18975             return;
18976         }
18977         
18978         function cleanAttr(n,v)
18979         {
18980             
18981             if (v.match(/^\./) || v.match(/^\//)) {
18982                 return;
18983             }
18984             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18985                 return;
18986             }
18987             if (v.match(/^#/)) {
18988                 return;
18989             }
18990 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18991             node.removeAttribute(n);
18992             
18993         }
18994         
18995         var cwhite = this.cwhite;
18996         var cblack = this.cblack;
18997             
18998         function cleanStyle(n,v)
18999         {
19000             if (v.match(/expression/)) { //XSS?? should we even bother..
19001                 node.removeAttribute(n);
19002                 return;
19003             }
19004             
19005             var parts = v.split(/;/);
19006             var clean = [];
19007             
19008             Roo.each(parts, function(p) {
19009                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19010                 if (!p.length) {
19011                     return true;
19012                 }
19013                 var l = p.split(':').shift().replace(/\s+/g,'');
19014                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19015                 
19016                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19017 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19018                     //node.removeAttribute(n);
19019                     return true;
19020                 }
19021                 //Roo.log()
19022                 // only allow 'c whitelisted system attributes'
19023                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19024 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19025                     //node.removeAttribute(n);
19026                     return true;
19027                 }
19028                 
19029                 
19030                  
19031                 
19032                 clean.push(p);
19033                 return true;
19034             });
19035             if (clean.length) { 
19036                 node.setAttribute(n, clean.join(';'));
19037             } else {
19038                 node.removeAttribute(n);
19039             }
19040             
19041         }
19042         
19043         
19044         for (var i = node.attributes.length-1; i > -1 ; i--) {
19045             var a = node.attributes[i];
19046             //console.log(a);
19047             
19048             if (a.name.toLowerCase().substr(0,2)=='on')  {
19049                 node.removeAttribute(a.name);
19050                 continue;
19051             }
19052             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19053                 node.removeAttribute(a.name);
19054                 continue;
19055             }
19056             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19057                 cleanAttr(a.name,a.value); // fixme..
19058                 continue;
19059             }
19060             if (a.name == 'style') {
19061                 cleanStyle(a.name,a.value);
19062                 continue;
19063             }
19064             /// clean up MS crap..
19065             // tecnically this should be a list of valid class'es..
19066             
19067             
19068             if (a.name == 'class') {
19069                 if (a.value.match(/^Mso/)) {
19070                     node.className = '';
19071                 }
19072                 
19073                 if (a.value.match(/body/)) {
19074                     node.className = '';
19075                 }
19076                 continue;
19077             }
19078             
19079             // style cleanup!?
19080             // class cleanup?
19081             
19082         }
19083         
19084         
19085         this.cleanUpChildren(node);
19086         
19087         
19088     },
19089     /**
19090      * Clean up MS wordisms...
19091      */
19092     cleanWord : function(node)
19093     {
19094         var _t = this;
19095         var cleanWordChildren = function()
19096         {
19097             if (!node.childNodes.length) {
19098                 return;
19099             }
19100             for (var i = node.childNodes.length-1; i > -1 ; i--) {
19101                _t.cleanWord(node.childNodes[i]);
19102             }
19103         }
19104         
19105         
19106         if (!node) {
19107             this.cleanWord(this.doc.body);
19108             return;
19109         }
19110         if (node.nodeName == "#text") {
19111             // clean up silly Windows -- stuff?
19112             return; 
19113         }
19114         if (node.nodeName == "#comment") {
19115             node.parentNode.removeChild(node);
19116             // clean up silly Windows -- stuff?
19117             return; 
19118         }
19119         
19120         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19121             node.parentNode.removeChild(node);
19122             return;
19123         }
19124         
19125         // remove - but keep children..
19126         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19127             while (node.childNodes.length) {
19128                 var cn = node.childNodes[0];
19129                 node.removeChild(cn);
19130                 node.parentNode.insertBefore(cn, node);
19131             }
19132             node.parentNode.removeChild(node);
19133             cleanWordChildren();
19134             return;
19135         }
19136         // clean styles
19137         if (node.className.length) {
19138             
19139             var cn = node.className.split(/\W+/);
19140             var cna = [];
19141             Roo.each(cn, function(cls) {
19142                 if (cls.match(/Mso[a-zA-Z]+/)) {
19143                     return;
19144                 }
19145                 cna.push(cls);
19146             });
19147             node.className = cna.length ? cna.join(' ') : '';
19148             if (!cna.length) {
19149                 node.removeAttribute("class");
19150             }
19151         }
19152         
19153         if (node.hasAttribute("lang")) {
19154             node.removeAttribute("lang");
19155         }
19156         
19157         if (node.hasAttribute("style")) {
19158             
19159             var styles = node.getAttribute("style").split(";");
19160             var nstyle = [];
19161             Roo.each(styles, function(s) {
19162                 if (!s.match(/:/)) {
19163                     return;
19164                 }
19165                 var kv = s.split(":");
19166                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19167                     return;
19168                 }
19169                 // what ever is left... we allow.
19170                 nstyle.push(s);
19171             });
19172             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19173             if (!nstyle.length) {
19174                 node.removeAttribute('style');
19175             }
19176         }
19177         
19178         cleanWordChildren();
19179         
19180         
19181     },
19182     domToHTML : function(currentElement, depth, nopadtext) {
19183         
19184         depth = depth || 0;
19185         nopadtext = nopadtext || false;
19186     
19187         if (!currentElement) {
19188             return this.domToHTML(this.doc.body);
19189         }
19190         
19191         //Roo.log(currentElement);
19192         var j;
19193         var allText = false;
19194         var nodeName = currentElement.nodeName;
19195         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19196         
19197         if  (nodeName == '#text') {
19198             
19199             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19200         }
19201         
19202         
19203         var ret = '';
19204         if (nodeName != 'BODY') {
19205              
19206             var i = 0;
19207             // Prints the node tagName, such as <A>, <IMG>, etc
19208             if (tagName) {
19209                 var attr = [];
19210                 for(i = 0; i < currentElement.attributes.length;i++) {
19211                     // quoting?
19212                     var aname = currentElement.attributes.item(i).name;
19213                     if (!currentElement.attributes.item(i).value.length) {
19214                         continue;
19215                     }
19216                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19217                 }
19218                 
19219                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19220             } 
19221             else {
19222                 
19223                 // eack
19224             }
19225         } else {
19226             tagName = false;
19227         }
19228         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19229             return ret;
19230         }
19231         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19232             nopadtext = true;
19233         }
19234         
19235         
19236         // Traverse the tree
19237         i = 0;
19238         var currentElementChild = currentElement.childNodes.item(i);
19239         var allText = true;
19240         var innerHTML  = '';
19241         lastnode = '';
19242         while (currentElementChild) {
19243             // Formatting code (indent the tree so it looks nice on the screen)
19244             var nopad = nopadtext;
19245             if (lastnode == 'SPAN') {
19246                 nopad  = true;
19247             }
19248             // text
19249             if  (currentElementChild.nodeName == '#text') {
19250                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19251                 toadd = nopadtext ? toadd : toadd.trim();
19252                 if (!nopad && toadd.length > 80) {
19253                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19254                 }
19255                 innerHTML  += toadd;
19256                 
19257                 i++;
19258                 currentElementChild = currentElement.childNodes.item(i);
19259                 lastNode = '';
19260                 continue;
19261             }
19262             allText = false;
19263             
19264             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19265                 
19266             // Recursively traverse the tree structure of the child node
19267             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19268             lastnode = currentElementChild.nodeName;
19269             i++;
19270             currentElementChild=currentElement.childNodes.item(i);
19271         }
19272         
19273         ret += innerHTML;
19274         
19275         if (!allText) {
19276                 // The remaining code is mostly for formatting the tree
19277             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19278         }
19279         
19280         
19281         if (tagName) {
19282             ret+= "</"+tagName+">";
19283         }
19284         return ret;
19285         
19286     },
19287         
19288     applyBlacklists : function()
19289     {
19290         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19291         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19292         
19293         this.white = [];
19294         this.black = [];
19295         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19296             if (b.indexOf(tag) > -1) {
19297                 return;
19298             }
19299             this.white.push(tag);
19300             
19301         }, this);
19302         
19303         Roo.each(w, function(tag) {
19304             if (b.indexOf(tag) > -1) {
19305                 return;
19306             }
19307             if (this.white.indexOf(tag) > -1) {
19308                 return;
19309             }
19310             this.white.push(tag);
19311             
19312         }, this);
19313         
19314         
19315         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19316             if (w.indexOf(tag) > -1) {
19317                 return;
19318             }
19319             this.black.push(tag);
19320             
19321         }, this);
19322         
19323         Roo.each(b, function(tag) {
19324             if (w.indexOf(tag) > -1) {
19325                 return;
19326             }
19327             if (this.black.indexOf(tag) > -1) {
19328                 return;
19329             }
19330             this.black.push(tag);
19331             
19332         }, this);
19333         
19334         
19335         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19336         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19337         
19338         this.cwhite = [];
19339         this.cblack = [];
19340         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19341             if (b.indexOf(tag) > -1) {
19342                 return;
19343             }
19344             this.cwhite.push(tag);
19345             
19346         }, this);
19347         
19348         Roo.each(w, function(tag) {
19349             if (b.indexOf(tag) > -1) {
19350                 return;
19351             }
19352             if (this.cwhite.indexOf(tag) > -1) {
19353                 return;
19354             }
19355             this.cwhite.push(tag);
19356             
19357         }, this);
19358         
19359         
19360         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19361             if (w.indexOf(tag) > -1) {
19362                 return;
19363             }
19364             this.cblack.push(tag);
19365             
19366         }, this);
19367         
19368         Roo.each(b, function(tag) {
19369             if (w.indexOf(tag) > -1) {
19370                 return;
19371             }
19372             if (this.cblack.indexOf(tag) > -1) {
19373                 return;
19374             }
19375             this.cblack.push(tag);
19376             
19377         }, this);
19378     },
19379     
19380     setStylesheets : function(stylesheets)
19381     {
19382         if(typeof(stylesheets) == 'string'){
19383             Roo.get(this.iframe.contentDocument.head).createChild({
19384                 tag : 'link',
19385                 rel : 'stylesheet',
19386                 type : 'text/css',
19387                 href : stylesheets
19388             });
19389             
19390             return;
19391         }
19392         var _this = this;
19393      
19394         Roo.each(stylesheets, function(s) {
19395             if(!s.length){
19396                 return;
19397             }
19398             
19399             Roo.get(_this.iframe.contentDocument.head).createChild({
19400                 tag : 'link',
19401                 rel : 'stylesheet',
19402                 type : 'text/css',
19403                 href : s
19404             });
19405         });
19406
19407         
19408     },
19409     
19410     removeStylesheets : function()
19411     {
19412         var _this = this;
19413         
19414         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19415             s.remove();
19416         });
19417     }
19418     
19419     // hide stuff that is not compatible
19420     /**
19421      * @event blur
19422      * @hide
19423      */
19424     /**
19425      * @event change
19426      * @hide
19427      */
19428     /**
19429      * @event focus
19430      * @hide
19431      */
19432     /**
19433      * @event specialkey
19434      * @hide
19435      */
19436     /**
19437      * @cfg {String} fieldClass @hide
19438      */
19439     /**
19440      * @cfg {String} focusClass @hide
19441      */
19442     /**
19443      * @cfg {String} autoCreate @hide
19444      */
19445     /**
19446      * @cfg {String} inputType @hide
19447      */
19448     /**
19449      * @cfg {String} invalidClass @hide
19450      */
19451     /**
19452      * @cfg {String} invalidText @hide
19453      */
19454     /**
19455      * @cfg {String} msgFx @hide
19456      */
19457     /**
19458      * @cfg {String} validateOnBlur @hide
19459      */
19460 });
19461
19462 Roo.HtmlEditorCore.white = [
19463         'area', 'br', 'img', 'input', 'hr', 'wbr',
19464         
19465        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19466        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19467        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19468        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19469        'table',   'ul',         'xmp', 
19470        
19471        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19472       'thead',   'tr', 
19473      
19474       'dir', 'menu', 'ol', 'ul', 'dl',
19475        
19476       'embed',  'object'
19477 ];
19478
19479
19480 Roo.HtmlEditorCore.black = [
19481     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19482         'applet', // 
19483         'base',   'basefont', 'bgsound', 'blink',  'body', 
19484         'frame',  'frameset', 'head',    'html',   'ilayer', 
19485         'iframe', 'layer',  'link',     'meta',    'object',   
19486         'script', 'style' ,'title',  'xml' // clean later..
19487 ];
19488 Roo.HtmlEditorCore.clean = [
19489     'script', 'style', 'title', 'xml'
19490 ];
19491 Roo.HtmlEditorCore.remove = [
19492     'font'
19493 ];
19494 // attributes..
19495
19496 Roo.HtmlEditorCore.ablack = [
19497     'on'
19498 ];
19499     
19500 Roo.HtmlEditorCore.aclean = [ 
19501     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19502 ];
19503
19504 // protocols..
19505 Roo.HtmlEditorCore.pwhite= [
19506         'http',  'https',  'mailto'
19507 ];
19508
19509 // white listed style attributes.
19510 Roo.HtmlEditorCore.cwhite= [
19511       //  'text-align', /// default is to allow most things..
19512       
19513          
19514 //        'font-size'//??
19515 ];
19516
19517 // black listed style attributes.
19518 Roo.HtmlEditorCore.cblack= [
19519       //  'font-size' -- this can be set by the project 
19520 ];
19521
19522
19523 Roo.HtmlEditorCore.swapCodes   =[ 
19524     [    8211, "--" ], 
19525     [    8212, "--" ], 
19526     [    8216,  "'" ],  
19527     [    8217, "'" ],  
19528     [    8220, '"' ],  
19529     [    8221, '"' ],  
19530     [    8226, "*" ],  
19531     [    8230, "..." ]
19532 ]; 
19533
19534     /*
19535  * - LGPL
19536  *
19537  * HtmlEditor
19538  * 
19539  */
19540
19541 /**
19542  * @class Roo.bootstrap.HtmlEditor
19543  * @extends Roo.bootstrap.TextArea
19544  * Bootstrap HtmlEditor class
19545
19546  * @constructor
19547  * Create a new HtmlEditor
19548  * @param {Object} config The config object
19549  */
19550
19551 Roo.bootstrap.HtmlEditor = function(config){
19552     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19553     if (!this.toolbars) {
19554         this.toolbars = [];
19555     }
19556     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19557     this.addEvents({
19558             /**
19559              * @event initialize
19560              * Fires when the editor is fully initialized (including the iframe)
19561              * @param {HtmlEditor} this
19562              */
19563             initialize: true,
19564             /**
19565              * @event activate
19566              * Fires when the editor is first receives the focus. Any insertion must wait
19567              * until after this event.
19568              * @param {HtmlEditor} this
19569              */
19570             activate: true,
19571              /**
19572              * @event beforesync
19573              * Fires before the textarea is updated with content from the editor iframe. Return false
19574              * to cancel the sync.
19575              * @param {HtmlEditor} this
19576              * @param {String} html
19577              */
19578             beforesync: true,
19579              /**
19580              * @event beforepush
19581              * Fires before the iframe editor is updated with content from the textarea. Return false
19582              * to cancel the push.
19583              * @param {HtmlEditor} this
19584              * @param {String} html
19585              */
19586             beforepush: true,
19587              /**
19588              * @event sync
19589              * Fires when the textarea is updated with content from the editor iframe.
19590              * @param {HtmlEditor} this
19591              * @param {String} html
19592              */
19593             sync: true,
19594              /**
19595              * @event push
19596              * Fires when the iframe editor is updated with content from the textarea.
19597              * @param {HtmlEditor} this
19598              * @param {String} html
19599              */
19600             push: true,
19601              /**
19602              * @event editmodechange
19603              * Fires when the editor switches edit modes
19604              * @param {HtmlEditor} this
19605              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19606              */
19607             editmodechange: true,
19608             /**
19609              * @event editorevent
19610              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19611              * @param {HtmlEditor} this
19612              */
19613             editorevent: true,
19614             /**
19615              * @event firstfocus
19616              * Fires when on first focus - needed by toolbars..
19617              * @param {HtmlEditor} this
19618              */
19619             firstfocus: true,
19620             /**
19621              * @event autosave
19622              * Auto save the htmlEditor value as a file into Events
19623              * @param {HtmlEditor} this
19624              */
19625             autosave: true,
19626             /**
19627              * @event savedpreview
19628              * preview the saved version of htmlEditor
19629              * @param {HtmlEditor} this
19630              */
19631             savedpreview: true
19632         });
19633 };
19634
19635
19636 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19637     
19638     
19639       /**
19640      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19641      */
19642     toolbars : false,
19643    
19644      /**
19645      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19646      *                        Roo.resizable.
19647      */
19648     resizable : false,
19649      /**
19650      * @cfg {Number} height (in pixels)
19651      */   
19652     height: 300,
19653    /**
19654      * @cfg {Number} width (in pixels)
19655      */   
19656     width: false,
19657     
19658     /**
19659      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19660      * 
19661      */
19662     stylesheets: false,
19663     
19664     // id of frame..
19665     frameId: false,
19666     
19667     // private properties
19668     validationEvent : false,
19669     deferHeight: true,
19670     initialized : false,
19671     activated : false,
19672     
19673     onFocus : Roo.emptyFn,
19674     iframePad:3,
19675     hideMode:'offsets',
19676     
19677     
19678     tbContainer : false,
19679     
19680     toolbarContainer :function() {
19681         return this.wrap.select('.x-html-editor-tb',true).first();
19682     },
19683
19684     /**
19685      * Protected method that will not generally be called directly. It
19686      * is called when the editor creates its toolbar. Override this method if you need to
19687      * add custom toolbar buttons.
19688      * @param {HtmlEditor} editor
19689      */
19690     createToolbar : function(){
19691         
19692         Roo.log("create toolbars");
19693         
19694         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19695         this.toolbars[0].render(this.toolbarContainer());
19696         
19697         return;
19698         
19699 //        if (!editor.toolbars || !editor.toolbars.length) {
19700 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19701 //        }
19702 //        
19703 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19704 //            editor.toolbars[i] = Roo.factory(
19705 //                    typeof(editor.toolbars[i]) == 'string' ?
19706 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19707 //                Roo.bootstrap.HtmlEditor);
19708 //            editor.toolbars[i].init(editor);
19709 //        }
19710     },
19711
19712      
19713     // private
19714     onRender : function(ct, position)
19715     {
19716        // Roo.log("Call onRender: " + this.xtype);
19717         var _t = this;
19718         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19719       
19720         this.wrap = this.inputEl().wrap({
19721             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19722         });
19723         
19724         this.editorcore.onRender(ct, position);
19725          
19726         if (this.resizable) {
19727             this.resizeEl = new Roo.Resizable(this.wrap, {
19728                 pinned : true,
19729                 wrap: true,
19730                 dynamic : true,
19731                 minHeight : this.height,
19732                 height: this.height,
19733                 handles : this.resizable,
19734                 width: this.width,
19735                 listeners : {
19736                     resize : function(r, w, h) {
19737                         _t.onResize(w,h); // -something
19738                     }
19739                 }
19740             });
19741             
19742         }
19743         this.createToolbar(this);
19744        
19745         
19746         if(!this.width && this.resizable){
19747             this.setSize(this.wrap.getSize());
19748         }
19749         if (this.resizeEl) {
19750             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19751             // should trigger onReize..
19752         }
19753         
19754     },
19755
19756     // private
19757     onResize : function(w, h)
19758     {
19759         Roo.log('resize: ' +w + ',' + h );
19760         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19761         var ew = false;
19762         var eh = false;
19763         
19764         if(this.inputEl() ){
19765             if(typeof w == 'number'){
19766                 var aw = w - this.wrap.getFrameWidth('lr');
19767                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19768                 ew = aw;
19769             }
19770             if(typeof h == 'number'){
19771                  var tbh = -11;  // fixme it needs to tool bar size!
19772                 for (var i =0; i < this.toolbars.length;i++) {
19773                     // fixme - ask toolbars for heights?
19774                     tbh += this.toolbars[i].el.getHeight();
19775                     //if (this.toolbars[i].footer) {
19776                     //    tbh += this.toolbars[i].footer.el.getHeight();
19777                     //}
19778                 }
19779               
19780                 
19781                 
19782                 
19783                 
19784                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19785                 ah -= 5; // knock a few pixes off for look..
19786                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19787                 var eh = ah;
19788             }
19789         }
19790         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19791         this.editorcore.onResize(ew,eh);
19792         
19793     },
19794
19795     /**
19796      * Toggles the editor between standard and source edit mode.
19797      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19798      */
19799     toggleSourceEdit : function(sourceEditMode)
19800     {
19801         this.editorcore.toggleSourceEdit(sourceEditMode);
19802         
19803         if(this.editorcore.sourceEditMode){
19804             Roo.log('editor - showing textarea');
19805             
19806 //            Roo.log('in');
19807 //            Roo.log(this.syncValue());
19808             this.syncValue();
19809             this.inputEl().removeClass(['hide', 'x-hidden']);
19810             this.inputEl().dom.removeAttribute('tabIndex');
19811             this.inputEl().focus();
19812         }else{
19813             Roo.log('editor - hiding textarea');
19814 //            Roo.log('out')
19815 //            Roo.log(this.pushValue()); 
19816             this.pushValue();
19817             
19818             this.inputEl().addClass(['hide', 'x-hidden']);
19819             this.inputEl().dom.setAttribute('tabIndex', -1);
19820             //this.deferFocus();
19821         }
19822          
19823         if(this.resizable){
19824             this.setSize(this.wrap.getSize());
19825         }
19826         
19827         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19828     },
19829  
19830     // private (for BoxComponent)
19831     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19832
19833     // private (for BoxComponent)
19834     getResizeEl : function(){
19835         return this.wrap;
19836     },
19837
19838     // private (for BoxComponent)
19839     getPositionEl : function(){
19840         return this.wrap;
19841     },
19842
19843     // private
19844     initEvents : function(){
19845         this.originalValue = this.getValue();
19846     },
19847
19848 //    /**
19849 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19850 //     * @method
19851 //     */
19852 //    markInvalid : Roo.emptyFn,
19853 //    /**
19854 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19855 //     * @method
19856 //     */
19857 //    clearInvalid : Roo.emptyFn,
19858
19859     setValue : function(v){
19860         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19861         this.editorcore.pushValue();
19862     },
19863
19864      
19865     // private
19866     deferFocus : function(){
19867         this.focus.defer(10, this);
19868     },
19869
19870     // doc'ed in Field
19871     focus : function(){
19872         this.editorcore.focus();
19873         
19874     },
19875       
19876
19877     // private
19878     onDestroy : function(){
19879         
19880         
19881         
19882         if(this.rendered){
19883             
19884             for (var i =0; i < this.toolbars.length;i++) {
19885                 // fixme - ask toolbars for heights?
19886                 this.toolbars[i].onDestroy();
19887             }
19888             
19889             this.wrap.dom.innerHTML = '';
19890             this.wrap.remove();
19891         }
19892     },
19893
19894     // private
19895     onFirstFocus : function(){
19896         //Roo.log("onFirstFocus");
19897         this.editorcore.onFirstFocus();
19898          for (var i =0; i < this.toolbars.length;i++) {
19899             this.toolbars[i].onFirstFocus();
19900         }
19901         
19902     },
19903     
19904     // private
19905     syncValue : function()
19906     {   
19907         this.editorcore.syncValue();
19908     },
19909     
19910     pushValue : function()
19911     {   
19912         this.editorcore.pushValue();
19913     }
19914      
19915     
19916     // hide stuff that is not compatible
19917     /**
19918      * @event blur
19919      * @hide
19920      */
19921     /**
19922      * @event change
19923      * @hide
19924      */
19925     /**
19926      * @event focus
19927      * @hide
19928      */
19929     /**
19930      * @event specialkey
19931      * @hide
19932      */
19933     /**
19934      * @cfg {String} fieldClass @hide
19935      */
19936     /**
19937      * @cfg {String} focusClass @hide
19938      */
19939     /**
19940      * @cfg {String} autoCreate @hide
19941      */
19942     /**
19943      * @cfg {String} inputType @hide
19944      */
19945     /**
19946      * @cfg {String} invalidClass @hide
19947      */
19948     /**
19949      * @cfg {String} invalidText @hide
19950      */
19951     /**
19952      * @cfg {String} msgFx @hide
19953      */
19954     /**
19955      * @cfg {String} validateOnBlur @hide
19956      */
19957 });
19958  
19959     
19960    
19961    
19962    
19963       
19964 Roo.namespace('Roo.bootstrap.htmleditor');
19965 /**
19966  * @class Roo.bootstrap.HtmlEditorToolbar1
19967  * Basic Toolbar
19968  * 
19969  * Usage:
19970  *
19971  new Roo.bootstrap.HtmlEditor({
19972     ....
19973     toolbars : [
19974         new Roo.bootstrap.HtmlEditorToolbar1({
19975             disable : { fonts: 1 , format: 1, ..., ... , ...],
19976             btns : [ .... ]
19977         })
19978     }
19979      
19980  * 
19981  * @cfg {Object} disable List of elements to disable..
19982  * @cfg {Array} btns List of additional buttons.
19983  * 
19984  * 
19985  * NEEDS Extra CSS? 
19986  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19987  */
19988  
19989 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19990 {
19991     
19992     Roo.apply(this, config);
19993     
19994     // default disabled, based on 'good practice'..
19995     this.disable = this.disable || {};
19996     Roo.applyIf(this.disable, {
19997         fontSize : true,
19998         colors : true,
19999         specialElements : true
20000     });
20001     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20002     
20003     this.editor = config.editor;
20004     this.editorcore = config.editor.editorcore;
20005     
20006     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20007     
20008     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20009     // dont call parent... till later.
20010 }
20011 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20012      
20013     bar : true,
20014     
20015     editor : false,
20016     editorcore : false,
20017     
20018     
20019     formats : [
20020         "p" ,  
20021         "h1","h2","h3","h4","h5","h6", 
20022         "pre", "code", 
20023         "abbr", "acronym", "address", "cite", "samp", "var",
20024         'div','span'
20025     ],
20026     
20027     onRender : function(ct, position)
20028     {
20029        // Roo.log("Call onRender: " + this.xtype);
20030         
20031        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20032        Roo.log(this.el);
20033        this.el.dom.style.marginBottom = '0';
20034        var _this = this;
20035        var editorcore = this.editorcore;
20036        var editor= this.editor;
20037        
20038        var children = [];
20039        var btn = function(id,cmd , toggle, handler){
20040        
20041             var  event = toggle ? 'toggle' : 'click';
20042        
20043             var a = {
20044                 size : 'sm',
20045                 xtype: 'Button',
20046                 xns: Roo.bootstrap,
20047                 glyphicon : id,
20048                 cmd : id || cmd,
20049                 enableToggle:toggle !== false,
20050                 //html : 'submit'
20051                 pressed : toggle ? false : null,
20052                 listeners : {}
20053             }
20054             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20055                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20056             }
20057             children.push(a);
20058             return a;
20059        }
20060         
20061         var style = {
20062                 xtype: 'Button',
20063                 size : 'sm',
20064                 xns: Roo.bootstrap,
20065                 glyphicon : 'font',
20066                 //html : 'submit'
20067                 menu : {
20068                     xtype: 'Menu',
20069                     xns: Roo.bootstrap,
20070                     items:  []
20071                 }
20072         };
20073         Roo.each(this.formats, function(f) {
20074             style.menu.items.push({
20075                 xtype :'MenuItem',
20076                 xns: Roo.bootstrap,
20077                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20078                 tagname : f,
20079                 listeners : {
20080                     click : function()
20081                     {
20082                         editorcore.insertTag(this.tagname);
20083                         editor.focus();
20084                     }
20085                 }
20086                 
20087             });
20088         });
20089          children.push(style);   
20090             
20091             
20092         btn('bold',false,true);
20093         btn('italic',false,true);
20094         btn('align-left', 'justifyleft',true);
20095         btn('align-center', 'justifycenter',true);
20096         btn('align-right' , 'justifyright',true);
20097         btn('link', false, false, function(btn) {
20098             //Roo.log("create link?");
20099             var url = prompt(this.createLinkText, this.defaultLinkValue);
20100             if(url && url != 'http:/'+'/'){
20101                 this.editorcore.relayCmd('createlink', url);
20102             }
20103         }),
20104         btn('list','insertunorderedlist',true);
20105         btn('pencil', false,true, function(btn){
20106                 Roo.log(this);
20107                 
20108                 this.toggleSourceEdit(btn.pressed);
20109         });
20110         /*
20111         var cog = {
20112                 xtype: 'Button',
20113                 size : 'sm',
20114                 xns: Roo.bootstrap,
20115                 glyphicon : 'cog',
20116                 //html : 'submit'
20117                 menu : {
20118                     xtype: 'Menu',
20119                     xns: Roo.bootstrap,
20120                     items:  []
20121                 }
20122         };
20123         
20124         cog.menu.items.push({
20125             xtype :'MenuItem',
20126             xns: Roo.bootstrap,
20127             html : Clean styles,
20128             tagname : f,
20129             listeners : {
20130                 click : function()
20131                 {
20132                     editorcore.insertTag(this.tagname);
20133                     editor.focus();
20134                 }
20135             }
20136             
20137         });
20138        */
20139         
20140          
20141        this.xtype = 'NavSimplebar';
20142         
20143         for(var i=0;i< children.length;i++) {
20144             
20145             this.buttons.add(this.addxtypeChild(children[i]));
20146             
20147         }
20148         
20149         editor.on('editorevent', this.updateToolbar, this);
20150     },
20151     onBtnClick : function(id)
20152     {
20153        this.editorcore.relayCmd(id);
20154        this.editorcore.focus();
20155     },
20156     
20157     /**
20158      * Protected method that will not generally be called directly. It triggers
20159      * a toolbar update by reading the markup state of the current selection in the editor.
20160      */
20161     updateToolbar: function(){
20162
20163         if(!this.editorcore.activated){
20164             this.editor.onFirstFocus(); // is this neeed?
20165             return;
20166         }
20167
20168         var btns = this.buttons; 
20169         var doc = this.editorcore.doc;
20170         btns.get('bold').setActive(doc.queryCommandState('bold'));
20171         btns.get('italic').setActive(doc.queryCommandState('italic'));
20172         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20173         
20174         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20175         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20176         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20177         
20178         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20179         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20180          /*
20181         
20182         var ans = this.editorcore.getAllAncestors();
20183         if (this.formatCombo) {
20184             
20185             
20186             var store = this.formatCombo.store;
20187             this.formatCombo.setValue("");
20188             for (var i =0; i < ans.length;i++) {
20189                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20190                     // select it..
20191                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20192                     break;
20193                 }
20194             }
20195         }
20196         
20197         
20198         
20199         // hides menus... - so this cant be on a menu...
20200         Roo.bootstrap.MenuMgr.hideAll();
20201         */
20202         Roo.bootstrap.MenuMgr.hideAll();
20203         //this.editorsyncValue();
20204     },
20205     onFirstFocus: function() {
20206         this.buttons.each(function(item){
20207            item.enable();
20208         });
20209     },
20210     toggleSourceEdit : function(sourceEditMode){
20211         
20212           
20213         if(sourceEditMode){
20214             Roo.log("disabling buttons");
20215            this.buttons.each( function(item){
20216                 if(item.cmd != 'pencil'){
20217                     item.disable();
20218                 }
20219             });
20220           
20221         }else{
20222             Roo.log("enabling buttons");
20223             if(this.editorcore.initialized){
20224                 this.buttons.each( function(item){
20225                     item.enable();
20226                 });
20227             }
20228             
20229         }
20230         Roo.log("calling toggole on editor");
20231         // tell the editor that it's been pressed..
20232         this.editor.toggleSourceEdit(sourceEditMode);
20233        
20234     }
20235 });
20236
20237
20238
20239
20240
20241 /**
20242  * @class Roo.bootstrap.Table.AbstractSelectionModel
20243  * @extends Roo.util.Observable
20244  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20245  * implemented by descendant classes.  This class should not be directly instantiated.
20246  * @constructor
20247  */
20248 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20249     this.locked = false;
20250     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20251 };
20252
20253
20254 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20255     /** @ignore Called by the grid automatically. Do not call directly. */
20256     init : function(grid){
20257         this.grid = grid;
20258         this.initEvents();
20259     },
20260
20261     /**
20262      * Locks the selections.
20263      */
20264     lock : function(){
20265         this.locked = true;
20266     },
20267
20268     /**
20269      * Unlocks the selections.
20270      */
20271     unlock : function(){
20272         this.locked = false;
20273     },
20274
20275     /**
20276      * Returns true if the selections are locked.
20277      * @return {Boolean}
20278      */
20279     isLocked : function(){
20280         return this.locked;
20281     }
20282 });
20283 /**
20284  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20285  * @class Roo.bootstrap.Table.RowSelectionModel
20286  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20287  * It supports multiple selections and keyboard selection/navigation. 
20288  * @constructor
20289  * @param {Object} config
20290  */
20291
20292 Roo.bootstrap.Table.RowSelectionModel = function(config){
20293     Roo.apply(this, config);
20294     this.selections = new Roo.util.MixedCollection(false, function(o){
20295         return o.id;
20296     });
20297
20298     this.last = false;
20299     this.lastActive = false;
20300
20301     this.addEvents({
20302         /**
20303              * @event selectionchange
20304              * Fires when the selection changes
20305              * @param {SelectionModel} this
20306              */
20307             "selectionchange" : true,
20308         /**
20309              * @event afterselectionchange
20310              * Fires after the selection changes (eg. by key press or clicking)
20311              * @param {SelectionModel} this
20312              */
20313             "afterselectionchange" : true,
20314         /**
20315              * @event beforerowselect
20316              * Fires when a row is selected being selected, return false to cancel.
20317              * @param {SelectionModel} this
20318              * @param {Number} rowIndex The selected index
20319              * @param {Boolean} keepExisting False if other selections will be cleared
20320              */
20321             "beforerowselect" : true,
20322         /**
20323              * @event rowselect
20324              * Fires when a row is selected.
20325              * @param {SelectionModel} this
20326              * @param {Number} rowIndex The selected index
20327              * @param {Roo.data.Record} r The record
20328              */
20329             "rowselect" : true,
20330         /**
20331              * @event rowdeselect
20332              * Fires when a row is deselected.
20333              * @param {SelectionModel} this
20334              * @param {Number} rowIndex The selected index
20335              */
20336         "rowdeselect" : true
20337     });
20338     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20339     this.locked = false;
20340 };
20341
20342 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20343     /**
20344      * @cfg {Boolean} singleSelect
20345      * True to allow selection of only one row at a time (defaults to false)
20346      */
20347     singleSelect : false,
20348
20349     // private
20350     initEvents : function(){
20351
20352         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20353             this.grid.on("mousedown", this.handleMouseDown, this);
20354         }else{ // allow click to work like normal
20355             this.grid.on("rowclick", this.handleDragableRowClick, this);
20356         }
20357
20358         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20359             "up" : function(e){
20360                 if(!e.shiftKey){
20361                     this.selectPrevious(e.shiftKey);
20362                 }else if(this.last !== false && this.lastActive !== false){
20363                     var last = this.last;
20364                     this.selectRange(this.last,  this.lastActive-1);
20365                     this.grid.getView().focusRow(this.lastActive);
20366                     if(last !== false){
20367                         this.last = last;
20368                     }
20369                 }else{
20370                     this.selectFirstRow();
20371                 }
20372                 this.fireEvent("afterselectionchange", this);
20373             },
20374             "down" : function(e){
20375                 if(!e.shiftKey){
20376                     this.selectNext(e.shiftKey);
20377                 }else if(this.last !== false && this.lastActive !== false){
20378                     var last = this.last;
20379                     this.selectRange(this.last,  this.lastActive+1);
20380                     this.grid.getView().focusRow(this.lastActive);
20381                     if(last !== false){
20382                         this.last = last;
20383                     }
20384                 }else{
20385                     this.selectFirstRow();
20386                 }
20387                 this.fireEvent("afterselectionchange", this);
20388             },
20389             scope: this
20390         });
20391
20392         var view = this.grid.view;
20393         view.on("refresh", this.onRefresh, this);
20394         view.on("rowupdated", this.onRowUpdated, this);
20395         view.on("rowremoved", this.onRemove, this);
20396     },
20397
20398     // private
20399     onRefresh : function(){
20400         var ds = this.grid.dataSource, i, v = this.grid.view;
20401         var s = this.selections;
20402         s.each(function(r){
20403             if((i = ds.indexOfId(r.id)) != -1){
20404                 v.onRowSelect(i);
20405             }else{
20406                 s.remove(r);
20407             }
20408         });
20409     },
20410
20411     // private
20412     onRemove : function(v, index, r){
20413         this.selections.remove(r);
20414     },
20415
20416     // private
20417     onRowUpdated : function(v, index, r){
20418         if(this.isSelected(r)){
20419             v.onRowSelect(index);
20420         }
20421     },
20422
20423     /**
20424      * Select records.
20425      * @param {Array} records The records to select
20426      * @param {Boolean} keepExisting (optional) True to keep existing selections
20427      */
20428     selectRecords : function(records, keepExisting){
20429         if(!keepExisting){
20430             this.clearSelections();
20431         }
20432         var ds = this.grid.dataSource;
20433         for(var i = 0, len = records.length; i < len; i++){
20434             this.selectRow(ds.indexOf(records[i]), true);
20435         }
20436     },
20437
20438     /**
20439      * Gets the number of selected rows.
20440      * @return {Number}
20441      */
20442     getCount : function(){
20443         return this.selections.length;
20444     },
20445
20446     /**
20447      * Selects the first row in the grid.
20448      */
20449     selectFirstRow : function(){
20450         this.selectRow(0);
20451     },
20452
20453     /**
20454      * Select the last row.
20455      * @param {Boolean} keepExisting (optional) True to keep existing selections
20456      */
20457     selectLastRow : function(keepExisting){
20458         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20459     },
20460
20461     /**
20462      * Selects the row immediately following the last selected row.
20463      * @param {Boolean} keepExisting (optional) True to keep existing selections
20464      */
20465     selectNext : function(keepExisting){
20466         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20467             this.selectRow(this.last+1, keepExisting);
20468             this.grid.getView().focusRow(this.last);
20469         }
20470     },
20471
20472     /**
20473      * Selects the row that precedes the last selected row.
20474      * @param {Boolean} keepExisting (optional) True to keep existing selections
20475      */
20476     selectPrevious : function(keepExisting){
20477         if(this.last){
20478             this.selectRow(this.last-1, keepExisting);
20479             this.grid.getView().focusRow(this.last);
20480         }
20481     },
20482
20483     /**
20484      * Returns the selected records
20485      * @return {Array} Array of selected records
20486      */
20487     getSelections : function(){
20488         return [].concat(this.selections.items);
20489     },
20490
20491     /**
20492      * Returns the first selected record.
20493      * @return {Record}
20494      */
20495     getSelected : function(){
20496         return this.selections.itemAt(0);
20497     },
20498
20499
20500     /**
20501      * Clears all selections.
20502      */
20503     clearSelections : function(fast){
20504         if(this.locked) return;
20505         if(fast !== true){
20506             var ds = this.grid.dataSource;
20507             var s = this.selections;
20508             s.each(function(r){
20509                 this.deselectRow(ds.indexOfId(r.id));
20510             }, this);
20511             s.clear();
20512         }else{
20513             this.selections.clear();
20514         }
20515         this.last = false;
20516     },
20517
20518
20519     /**
20520      * Selects all rows.
20521      */
20522     selectAll : function(){
20523         if(this.locked) return;
20524         this.selections.clear();
20525         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20526             this.selectRow(i, true);
20527         }
20528     },
20529
20530     /**
20531      * Returns True if there is a selection.
20532      * @return {Boolean}
20533      */
20534     hasSelection : function(){
20535         return this.selections.length > 0;
20536     },
20537
20538     /**
20539      * Returns True if the specified row is selected.
20540      * @param {Number/Record} record The record or index of the record to check
20541      * @return {Boolean}
20542      */
20543     isSelected : function(index){
20544         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20545         return (r && this.selections.key(r.id) ? true : false);
20546     },
20547
20548     /**
20549      * Returns True if the specified record id is selected.
20550      * @param {String} id The id of record to check
20551      * @return {Boolean}
20552      */
20553     isIdSelected : function(id){
20554         return (this.selections.key(id) ? true : false);
20555     },
20556
20557     // private
20558     handleMouseDown : function(e, t){
20559         var view = this.grid.getView(), rowIndex;
20560         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20561             return;
20562         };
20563         if(e.shiftKey && this.last !== false){
20564             var last = this.last;
20565             this.selectRange(last, rowIndex, e.ctrlKey);
20566             this.last = last; // reset the last
20567             view.focusRow(rowIndex);
20568         }else{
20569             var isSelected = this.isSelected(rowIndex);
20570             if(e.button !== 0 && isSelected){
20571                 view.focusRow(rowIndex);
20572             }else if(e.ctrlKey && isSelected){
20573                 this.deselectRow(rowIndex);
20574             }else if(!isSelected){
20575                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20576                 view.focusRow(rowIndex);
20577             }
20578         }
20579         this.fireEvent("afterselectionchange", this);
20580     },
20581     // private
20582     handleDragableRowClick :  function(grid, rowIndex, e) 
20583     {
20584         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20585             this.selectRow(rowIndex, false);
20586             grid.view.focusRow(rowIndex);
20587              this.fireEvent("afterselectionchange", this);
20588         }
20589     },
20590     
20591     /**
20592      * Selects multiple rows.
20593      * @param {Array} rows Array of the indexes of the row to select
20594      * @param {Boolean} keepExisting (optional) True to keep existing selections
20595      */
20596     selectRows : function(rows, keepExisting){
20597         if(!keepExisting){
20598             this.clearSelections();
20599         }
20600         for(var i = 0, len = rows.length; i < len; i++){
20601             this.selectRow(rows[i], true);
20602         }
20603     },
20604
20605     /**
20606      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20607      * @param {Number} startRow The index of the first row in the range
20608      * @param {Number} endRow The index of the last row in the range
20609      * @param {Boolean} keepExisting (optional) True to retain existing selections
20610      */
20611     selectRange : function(startRow, endRow, keepExisting){
20612         if(this.locked) return;
20613         if(!keepExisting){
20614             this.clearSelections();
20615         }
20616         if(startRow <= endRow){
20617             for(var i = startRow; i <= endRow; i++){
20618                 this.selectRow(i, true);
20619             }
20620         }else{
20621             for(var i = startRow; i >= endRow; i--){
20622                 this.selectRow(i, true);
20623             }
20624         }
20625     },
20626
20627     /**
20628      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20629      * @param {Number} startRow The index of the first row in the range
20630      * @param {Number} endRow The index of the last row in the range
20631      */
20632     deselectRange : function(startRow, endRow, preventViewNotify){
20633         if(this.locked) return;
20634         for(var i = startRow; i <= endRow; i++){
20635             this.deselectRow(i, preventViewNotify);
20636         }
20637     },
20638
20639     /**
20640      * Selects a row.
20641      * @param {Number} row The index of the row to select
20642      * @param {Boolean} keepExisting (optional) True to keep existing selections
20643      */
20644     selectRow : function(index, keepExisting, preventViewNotify){
20645         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20646         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20647             if(!keepExisting || this.singleSelect){
20648                 this.clearSelections();
20649             }
20650             var r = this.grid.dataSource.getAt(index);
20651             this.selections.add(r);
20652             this.last = this.lastActive = index;
20653             if(!preventViewNotify){
20654                 this.grid.getView().onRowSelect(index);
20655             }
20656             this.fireEvent("rowselect", this, index, r);
20657             this.fireEvent("selectionchange", this);
20658         }
20659     },
20660
20661     /**
20662      * Deselects a row.
20663      * @param {Number} row The index of the row to deselect
20664      */
20665     deselectRow : function(index, preventViewNotify){
20666         if(this.locked) return;
20667         if(this.last == index){
20668             this.last = false;
20669         }
20670         if(this.lastActive == index){
20671             this.lastActive = false;
20672         }
20673         var r = this.grid.dataSource.getAt(index);
20674         this.selections.remove(r);
20675         if(!preventViewNotify){
20676             this.grid.getView().onRowDeselect(index);
20677         }
20678         this.fireEvent("rowdeselect", this, index);
20679         this.fireEvent("selectionchange", this);
20680     },
20681
20682     // private
20683     restoreLast : function(){
20684         if(this._last){
20685             this.last = this._last;
20686         }
20687     },
20688
20689     // private
20690     acceptsNav : function(row, col, cm){
20691         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20692     },
20693
20694     // private
20695     onEditorKey : function(field, e){
20696         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20697         if(k == e.TAB){
20698             e.stopEvent();
20699             ed.completeEdit();
20700             if(e.shiftKey){
20701                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20702             }else{
20703                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20704             }
20705         }else if(k == e.ENTER && !e.ctrlKey){
20706             e.stopEvent();
20707             ed.completeEdit();
20708             if(e.shiftKey){
20709                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20710             }else{
20711                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20712             }
20713         }else if(k == e.ESC){
20714             ed.cancelEdit();
20715         }
20716         if(newCell){
20717             g.startEditing(newCell[0], newCell[1]);
20718         }
20719     }
20720 });/*
20721  * Based on:
20722  * Ext JS Library 1.1.1
20723  * Copyright(c) 2006-2007, Ext JS, LLC.
20724  *
20725  * Originally Released Under LGPL - original licence link has changed is not relivant.
20726  *
20727  * Fork - LGPL
20728  * <script type="text/javascript">
20729  */
20730  
20731 /**
20732  * @class Roo.bootstrap.PagingToolbar
20733  * @extends Roo.Row
20734  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20735  * @constructor
20736  * Create a new PagingToolbar
20737  * @param {Object} config The config object
20738  */
20739 Roo.bootstrap.PagingToolbar = function(config)
20740 {
20741     // old args format still supported... - xtype is prefered..
20742         // created from xtype...
20743     var ds = config.dataSource;
20744     this.toolbarItems = [];
20745     if (config.items) {
20746         this.toolbarItems = config.items;
20747 //        config.items = [];
20748     }
20749     
20750     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20751     this.ds = ds;
20752     this.cursor = 0;
20753     if (ds) { 
20754         this.bind(ds);
20755     }
20756     
20757     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20758     
20759 };
20760
20761 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20762     /**
20763      * @cfg {Roo.data.Store} dataSource
20764      * The underlying data store providing the paged data
20765      */
20766     /**
20767      * @cfg {String/HTMLElement/Element} container
20768      * container The id or element that will contain the toolbar
20769      */
20770     /**
20771      * @cfg {Boolean} displayInfo
20772      * True to display the displayMsg (defaults to false)
20773      */
20774     /**
20775      * @cfg {Number} pageSize
20776      * The number of records to display per page (defaults to 20)
20777      */
20778     pageSize: 20,
20779     /**
20780      * @cfg {String} displayMsg
20781      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20782      */
20783     displayMsg : 'Displaying {0} - {1} of {2}',
20784     /**
20785      * @cfg {String} emptyMsg
20786      * The message to display when no records are found (defaults to "No data to display")
20787      */
20788     emptyMsg : 'No data to display',
20789     /**
20790      * Customizable piece of the default paging text (defaults to "Page")
20791      * @type String
20792      */
20793     beforePageText : "Page",
20794     /**
20795      * Customizable piece of the default paging text (defaults to "of %0")
20796      * @type String
20797      */
20798     afterPageText : "of {0}",
20799     /**
20800      * Customizable piece of the default paging text (defaults to "First Page")
20801      * @type String
20802      */
20803     firstText : "First Page",
20804     /**
20805      * Customizable piece of the default paging text (defaults to "Previous Page")
20806      * @type String
20807      */
20808     prevText : "Previous Page",
20809     /**
20810      * Customizable piece of the default paging text (defaults to "Next Page")
20811      * @type String
20812      */
20813     nextText : "Next Page",
20814     /**
20815      * Customizable piece of the default paging text (defaults to "Last Page")
20816      * @type String
20817      */
20818     lastText : "Last Page",
20819     /**
20820      * Customizable piece of the default paging text (defaults to "Refresh")
20821      * @type String
20822      */
20823     refreshText : "Refresh",
20824
20825     buttons : false,
20826     // private
20827     onRender : function(ct, position) 
20828     {
20829         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20830         this.navgroup.parentId = this.id;
20831         this.navgroup.onRender(this.el, null);
20832         // add the buttons to the navgroup
20833         
20834         if(this.displayInfo){
20835             Roo.log(this.el.select('ul.navbar-nav',true).first());
20836             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20837             this.displayEl = this.el.select('.x-paging-info', true).first();
20838 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20839 //            this.displayEl = navel.el.select('span',true).first();
20840         }
20841         
20842         var _this = this;
20843         
20844         if(this.buttons){
20845             Roo.each(_this.buttons, function(e){
20846                Roo.factory(e).onRender(_this.el, null);
20847             });
20848         }
20849             
20850         Roo.each(_this.toolbarItems, function(e) {
20851             _this.navgroup.addItem(e);
20852         });
20853         
20854         
20855         this.first = this.navgroup.addItem({
20856             tooltip: this.firstText,
20857             cls: "prev",
20858             icon : 'fa fa-backward',
20859             disabled: true,
20860             preventDefault: true,
20861             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20862         });
20863         
20864         this.prev =  this.navgroup.addItem({
20865             tooltip: this.prevText,
20866             cls: "prev",
20867             icon : 'fa fa-step-backward',
20868             disabled: true,
20869             preventDefault: true,
20870             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20871         });
20872     //this.addSeparator();
20873         
20874         
20875         var field = this.navgroup.addItem( {
20876             tagtype : 'span',
20877             cls : 'x-paging-position',
20878             
20879             html : this.beforePageText  +
20880                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20881                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20882          } ); //?? escaped?
20883         
20884         this.field = field.el.select('input', true).first();
20885         this.field.on("keydown", this.onPagingKeydown, this);
20886         this.field.on("focus", function(){this.dom.select();});
20887     
20888     
20889         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20890         //this.field.setHeight(18);
20891         //this.addSeparator();
20892         this.next = this.navgroup.addItem({
20893             tooltip: this.nextText,
20894             cls: "next",
20895             html : ' <i class="fa fa-step-forward">',
20896             disabled: true,
20897             preventDefault: true,
20898             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20899         });
20900         this.last = this.navgroup.addItem({
20901             tooltip: this.lastText,
20902             icon : 'fa fa-forward',
20903             cls: "next",
20904             disabled: true,
20905             preventDefault: true,
20906             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20907         });
20908     //this.addSeparator();
20909         this.loading = this.navgroup.addItem({
20910             tooltip: this.refreshText,
20911             icon: 'fa fa-refresh',
20912             preventDefault: true,
20913             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20914         });
20915
20916     },
20917
20918     // private
20919     updateInfo : function(){
20920         if(this.displayEl){
20921             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20922             var msg = count == 0 ?
20923                 this.emptyMsg :
20924                 String.format(
20925                     this.displayMsg,
20926                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20927                 );
20928             this.displayEl.update(msg);
20929         }
20930     },
20931
20932     // private
20933     onLoad : function(ds, r, o){
20934        this.cursor = o.params ? o.params.start : 0;
20935        var d = this.getPageData(),
20936             ap = d.activePage,
20937             ps = d.pages;
20938         
20939        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20940        this.field.dom.value = ap;
20941        this.first.setDisabled(ap == 1);
20942        this.prev.setDisabled(ap == 1);
20943        this.next.setDisabled(ap == ps);
20944        this.last.setDisabled(ap == ps);
20945        this.loading.enable();
20946        this.updateInfo();
20947     },
20948
20949     // private
20950     getPageData : function(){
20951         var total = this.ds.getTotalCount();
20952         return {
20953             total : total,
20954             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20955             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20956         };
20957     },
20958
20959     // private
20960     onLoadError : function(){
20961         this.loading.enable();
20962     },
20963
20964     // private
20965     onPagingKeydown : function(e){
20966         var k = e.getKey();
20967         var d = this.getPageData();
20968         if(k == e.RETURN){
20969             var v = this.field.dom.value, pageNum;
20970             if(!v || isNaN(pageNum = parseInt(v, 10))){
20971                 this.field.dom.value = d.activePage;
20972                 return;
20973             }
20974             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20975             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20976             e.stopEvent();
20977         }
20978         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))
20979         {
20980           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20981           this.field.dom.value = pageNum;
20982           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20983           e.stopEvent();
20984         }
20985         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20986         {
20987           var v = this.field.dom.value, pageNum; 
20988           var increment = (e.shiftKey) ? 10 : 1;
20989           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20990             increment *= -1;
20991           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20992             this.field.dom.value = d.activePage;
20993             return;
20994           }
20995           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20996           {
20997             this.field.dom.value = parseInt(v, 10) + increment;
20998             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20999             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21000           }
21001           e.stopEvent();
21002         }
21003     },
21004
21005     // private
21006     beforeLoad : function(){
21007         if(this.loading){
21008             this.loading.disable();
21009         }
21010     },
21011
21012     // private
21013     onClick : function(which){
21014         
21015         var ds = this.ds;
21016         if (!ds) {
21017             return;
21018         }
21019         
21020         switch(which){
21021             case "first":
21022                 ds.load({params:{start: 0, limit: this.pageSize}});
21023             break;
21024             case "prev":
21025                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21026             break;
21027             case "next":
21028                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21029             break;
21030             case "last":
21031                 var total = ds.getTotalCount();
21032                 var extra = total % this.pageSize;
21033                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21034                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21035             break;
21036             case "refresh":
21037                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21038             break;
21039         }
21040     },
21041
21042     /**
21043      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21044      * @param {Roo.data.Store} store The data store to unbind
21045      */
21046     unbind : function(ds){
21047         ds.un("beforeload", this.beforeLoad, this);
21048         ds.un("load", this.onLoad, this);
21049         ds.un("loadexception", this.onLoadError, this);
21050         ds.un("remove", this.updateInfo, this);
21051         ds.un("add", this.updateInfo, this);
21052         this.ds = undefined;
21053     },
21054
21055     /**
21056      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21057      * @param {Roo.data.Store} store The data store to bind
21058      */
21059     bind : function(ds){
21060         ds.on("beforeload", this.beforeLoad, this);
21061         ds.on("load", this.onLoad, this);
21062         ds.on("loadexception", this.onLoadError, this);
21063         ds.on("remove", this.updateInfo, this);
21064         ds.on("add", this.updateInfo, this);
21065         this.ds = ds;
21066     }
21067 });/*
21068  * - LGPL
21069  *
21070  * element
21071  * 
21072  */
21073
21074 /**
21075  * @class Roo.bootstrap.MessageBar
21076  * @extends Roo.bootstrap.Component
21077  * Bootstrap MessageBar class
21078  * @cfg {String} html contents of the MessageBar
21079  * @cfg {String} weight (info | success | warning | danger) default info
21080  * @cfg {String} beforeClass insert the bar before the given class
21081  * @cfg {Boolean} closable (true | false) default false
21082  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21083  * 
21084  * @constructor
21085  * Create a new Element
21086  * @param {Object} config The config object
21087  */
21088
21089 Roo.bootstrap.MessageBar = function(config){
21090     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21091 };
21092
21093 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21094     
21095     html: '',
21096     weight: 'info',
21097     closable: false,
21098     fixed: false,
21099     beforeClass: 'bootstrap-sticky-wrap',
21100     
21101     getAutoCreate : function(){
21102         
21103         var cfg = {
21104             tag: 'div',
21105             cls: 'alert alert-dismissable alert-' + this.weight,
21106             cn: [
21107                 {
21108                     tag: 'span',
21109                     cls: 'message',
21110                     html: this.html || ''
21111                 }
21112             ]
21113         }
21114         
21115         if(this.fixed){
21116             cfg.cls += ' alert-messages-fixed';
21117         }
21118         
21119         if(this.closable){
21120             cfg.cn.push({
21121                 tag: 'button',
21122                 cls: 'close',
21123                 html: 'x'
21124             });
21125         }
21126         
21127         return cfg;
21128     },
21129     
21130     onRender : function(ct, position)
21131     {
21132         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21133         
21134         if(!this.el){
21135             var cfg = Roo.apply({},  this.getAutoCreate());
21136             cfg.id = Roo.id();
21137             
21138             if (this.cls) {
21139                 cfg.cls += ' ' + this.cls;
21140             }
21141             if (this.style) {
21142                 cfg.style = this.style;
21143             }
21144             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21145             
21146             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21147         }
21148         
21149         this.el.select('>button.close').on('click', this.hide, this);
21150         
21151     },
21152     
21153     show : function()
21154     {
21155         if (!this.rendered) {
21156             this.render();
21157         }
21158         
21159         this.el.show();
21160         
21161         this.fireEvent('show', this);
21162         
21163     },
21164     
21165     hide : function()
21166     {
21167         if (!this.rendered) {
21168             this.render();
21169         }
21170         
21171         this.el.hide();
21172         
21173         this.fireEvent('hide', this);
21174     },
21175     
21176     update : function()
21177     {
21178 //        var e = this.el.dom.firstChild;
21179 //        
21180 //        if(this.closable){
21181 //            e = e.nextSibling;
21182 //        }
21183 //        
21184 //        e.data = this.html || '';
21185
21186         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21187     }
21188    
21189 });
21190
21191  
21192
21193      /*
21194  * - LGPL
21195  *
21196  * Graph
21197  * 
21198  */
21199
21200
21201 /**
21202  * @class Roo.bootstrap.Graph
21203  * @extends Roo.bootstrap.Component
21204  * Bootstrap Graph class
21205 > Prameters
21206  -sm {number} sm 4
21207  -md {number} md 5
21208  @cfg {String} graphtype  bar | vbar | pie
21209  @cfg {number} g_x coodinator | centre x (pie)
21210  @cfg {number} g_y coodinator | centre y (pie)
21211  @cfg {number} g_r radius (pie)
21212  @cfg {number} g_height height of the chart (respected by all elements in the set)
21213  @cfg {number} g_width width of the chart (respected by all elements in the set)
21214  @cfg {Object} title The title of the chart
21215     
21216  -{Array}  values
21217  -opts (object) options for the chart 
21218      o {
21219      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21220      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21221      o vgutter (number)
21222      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.
21223      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21224      o to
21225      o stretch (boolean)
21226      o }
21227  -opts (object) options for the pie
21228      o{
21229      o cut
21230      o startAngle (number)
21231      o endAngle (number)
21232      } 
21233  *
21234  * @constructor
21235  * Create a new Input
21236  * @param {Object} config The config object
21237  */
21238
21239 Roo.bootstrap.Graph = function(config){
21240     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21241     
21242     this.addEvents({
21243         // img events
21244         /**
21245          * @event click
21246          * The img click event for the img.
21247          * @param {Roo.EventObject} e
21248          */
21249         "click" : true
21250     });
21251 };
21252
21253 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21254     
21255     sm: 4,
21256     md: 5,
21257     graphtype: 'bar',
21258     g_height: 250,
21259     g_width: 400,
21260     g_x: 50,
21261     g_y: 50,
21262     g_r: 30,
21263     opts:{
21264         //g_colors: this.colors,
21265         g_type: 'soft',
21266         g_gutter: '20%'
21267
21268     },
21269     title : false,
21270
21271     getAutoCreate : function(){
21272         
21273         var cfg = {
21274             tag: 'div',
21275             html : null
21276         }
21277         
21278         
21279         return  cfg;
21280     },
21281
21282     onRender : function(ct,position){
21283         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21284         this.raphael = Raphael(this.el.dom);
21285         
21286                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21287                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21288                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21289                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21290                 /*
21291                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21292                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21293                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21294                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21295                 
21296                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21297                 r.barchart(330, 10, 300, 220, data1);
21298                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21299                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21300                 */
21301                 
21302                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21303                 // r.barchart(30, 30, 560, 250,  xdata, {
21304                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21305                 //     axis : "0 0 1 1",
21306                 //     axisxlabels :  xdata
21307                 //     //yvalues : cols,
21308                    
21309                 // });
21310 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21311 //        
21312 //        this.load(null,xdata,{
21313 //                axis : "0 0 1 1",
21314 //                axisxlabels :  xdata
21315 //                });
21316
21317     },
21318
21319     load : function(graphtype,xdata,opts){
21320         this.raphael.clear();
21321         if(!graphtype) {
21322             graphtype = this.graphtype;
21323         }
21324         if(!opts){
21325             opts = this.opts;
21326         }
21327         var r = this.raphael,
21328             fin = function () {
21329                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21330             },
21331             fout = function () {
21332                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21333             },
21334             pfin = function() {
21335                 this.sector.stop();
21336                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21337
21338                 if (this.label) {
21339                     this.label[0].stop();
21340                     this.label[0].attr({ r: 7.5 });
21341                     this.label[1].attr({ "font-weight": 800 });
21342                 }
21343             },
21344             pfout = function() {
21345                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21346
21347                 if (this.label) {
21348                     this.label[0].animate({ r: 5 }, 500, "bounce");
21349                     this.label[1].attr({ "font-weight": 400 });
21350                 }
21351             };
21352
21353         switch(graphtype){
21354             case 'bar':
21355                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21356                 break;
21357             case 'hbar':
21358                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21359                 break;
21360             case 'pie':
21361 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21362 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21363 //            
21364                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21365                 
21366                 break;
21367
21368         }
21369         
21370         if(this.title){
21371             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21372         }
21373         
21374     },
21375     
21376     setTitle: function(o)
21377     {
21378         this.title = o;
21379     },
21380     
21381     initEvents: function() {
21382         
21383         if(!this.href){
21384             this.el.on('click', this.onClick, this);
21385         }
21386     },
21387     
21388     onClick : function(e)
21389     {
21390         Roo.log('img onclick');
21391         this.fireEvent('click', this, e);
21392     }
21393    
21394 });
21395
21396  
21397 /*
21398  * - LGPL
21399  *
21400  * numberBox
21401  * 
21402  */
21403 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21404
21405 /**
21406  * @class Roo.bootstrap.dash.NumberBox
21407  * @extends Roo.bootstrap.Component
21408  * Bootstrap NumberBox class
21409  * @cfg {String} headline Box headline
21410  * @cfg {String} content Box content
21411  * @cfg {String} icon Box icon
21412  * @cfg {String} footer Footer text
21413  * @cfg {String} fhref Footer href
21414  * 
21415  * @constructor
21416  * Create a new NumberBox
21417  * @param {Object} config The config object
21418  */
21419
21420
21421 Roo.bootstrap.dash.NumberBox = function(config){
21422     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21423     
21424 };
21425
21426 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21427     
21428     headline : '',
21429     content : '',
21430     icon : '',
21431     footer : '',
21432     fhref : '',
21433     ficon : '',
21434     
21435     getAutoCreate : function(){
21436         
21437         var cfg = {
21438             tag : 'div',
21439             cls : 'small-box ',
21440             cn : [
21441                 {
21442                     tag : 'div',
21443                     cls : 'inner',
21444                     cn :[
21445                         {
21446                             tag : 'h3',
21447                             cls : 'roo-headline',
21448                             html : this.headline
21449                         },
21450                         {
21451                             tag : 'p',
21452                             cls : 'roo-content',
21453                             html : this.content
21454                         }
21455                     ]
21456                 }
21457             ]
21458         }
21459         
21460         if(this.icon){
21461             cfg.cn.push({
21462                 tag : 'div',
21463                 cls : 'icon',
21464                 cn :[
21465                     {
21466                         tag : 'i',
21467                         cls : 'ion ' + this.icon
21468                     }
21469                 ]
21470             });
21471         }
21472         
21473         if(this.footer){
21474             var footer = {
21475                 tag : 'a',
21476                 cls : 'small-box-footer',
21477                 href : this.fhref || '#',
21478                 html : this.footer
21479             };
21480             
21481             cfg.cn.push(footer);
21482             
21483         }
21484         
21485         return  cfg;
21486     },
21487
21488     onRender : function(ct,position){
21489         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21490
21491
21492        
21493                 
21494     },
21495
21496     setHeadline: function (value)
21497     {
21498         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21499     },
21500     
21501     setFooter: function (value, href)
21502     {
21503         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21504         
21505         if(href){
21506             this.el.select('a.small-box-footer',true).first().attr('href', href);
21507         }
21508         
21509     },
21510
21511     setContent: function (value)
21512     {
21513         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21514     },
21515
21516     initEvents: function() 
21517     {   
21518         
21519     }
21520     
21521 });
21522
21523  
21524 /*
21525  * - LGPL
21526  *
21527  * TabBox
21528  * 
21529  */
21530 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21531
21532 /**
21533  * @class Roo.bootstrap.dash.TabBox
21534  * @extends Roo.bootstrap.Component
21535  * Bootstrap TabBox class
21536  * @cfg {String} title Title of the TabBox
21537  * @cfg {String} icon Icon of the TabBox
21538  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21539  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21540  * 
21541  * @constructor
21542  * Create a new TabBox
21543  * @param {Object} config The config object
21544  */
21545
21546
21547 Roo.bootstrap.dash.TabBox = function(config){
21548     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21549     this.addEvents({
21550         // raw events
21551         /**
21552          * @event addpane
21553          * When a pane is added
21554          * @param {Roo.bootstrap.dash.TabPane} pane
21555          */
21556         "addpane" : true,
21557         /**
21558          * @event activatepane
21559          * When a pane is activated
21560          * @param {Roo.bootstrap.dash.TabPane} pane
21561          */
21562         "activatepane" : true
21563         
21564          
21565     });
21566     
21567     this.panes = [];
21568 };
21569
21570 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21571
21572     title : '',
21573     icon : false,
21574     showtabs : true,
21575     tabScrollable : false,
21576     
21577     getChildContainer : function()
21578     {
21579         return this.el.select('.tab-content', true).first();
21580     },
21581     
21582     getAutoCreate : function(){
21583         
21584         var header = {
21585             tag: 'li',
21586             cls: 'pull-left header',
21587             html: this.title,
21588             cn : []
21589         };
21590         
21591         if(this.icon){
21592             header.cn.push({
21593                 tag: 'i',
21594                 cls: 'fa ' + this.icon
21595             });
21596         }
21597         
21598         var h = {
21599             tag: 'ul',
21600             cls: 'nav nav-tabs pull-right',
21601             cn: [
21602                 header
21603             ]
21604         };
21605         
21606         if(this.tabScrollable){
21607             h = {
21608                 tag: 'div',
21609                 cls: 'tab-header',
21610                 cn: [
21611                     {
21612                         tag: 'ul',
21613                         cls: 'nav nav-tabs pull-right',
21614                         cn: [
21615                             header
21616                         ]
21617                     }
21618                 ]
21619             }
21620         }
21621         
21622         var cfg = {
21623             tag: 'div',
21624             cls: 'nav-tabs-custom',
21625             cn: [
21626                 h,
21627                 {
21628                     tag: 'div',
21629                     cls: 'tab-content no-padding',
21630                     cn: []
21631                 }
21632             ]
21633         }
21634
21635         return  cfg;
21636     },
21637     initEvents : function()
21638     {
21639         //Roo.log('add add pane handler');
21640         this.on('addpane', this.onAddPane, this);
21641     },
21642      /**
21643      * Updates the box title
21644      * @param {String} html to set the title to.
21645      */
21646     setTitle : function(value)
21647     {
21648         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21649     },
21650     onAddPane : function(pane)
21651     {
21652         this.panes.push(pane);
21653         //Roo.log('addpane');
21654         //Roo.log(pane);
21655         // tabs are rendere left to right..
21656         if(!this.showtabs){
21657             return;
21658         }
21659         
21660         var ctr = this.el.select('.nav-tabs', true).first();
21661          
21662          
21663         var existing = ctr.select('.nav-tab',true);
21664         var qty = existing.getCount();;
21665         
21666         
21667         var tab = ctr.createChild({
21668             tag : 'li',
21669             cls : 'nav-tab' + (qty ? '' : ' active'),
21670             cn : [
21671                 {
21672                     tag : 'a',
21673                     href:'#',
21674                     html : pane.title
21675                 }
21676             ]
21677         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21678         pane.tab = tab;
21679         
21680         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21681         if (!qty) {
21682             pane.el.addClass('active');
21683         }
21684         
21685                 
21686     },
21687     onTabClick : function(ev,un,ob,pane)
21688     {
21689         //Roo.log('tab - prev default');
21690         ev.preventDefault();
21691         
21692         
21693         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21694         pane.tab.addClass('active');
21695         //Roo.log(pane.title);
21696         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21697         // technically we should have a deactivate event.. but maybe add later.
21698         // and it should not de-activate the selected tab...
21699         this.fireEvent('activatepane', pane);
21700         pane.el.addClass('active');
21701         pane.fireEvent('activate');
21702         
21703         
21704     },
21705     
21706     getActivePane : function()
21707     {
21708         var r = false;
21709         Roo.each(this.panes, function(p) {
21710             if(p.el.hasClass('active')){
21711                 r = p;
21712                 return false;
21713             }
21714             
21715             return;
21716         });
21717         
21718         return r;
21719     }
21720     
21721     
21722 });
21723
21724  
21725 /*
21726  * - LGPL
21727  *
21728  * Tab pane
21729  * 
21730  */
21731 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21732 /**
21733  * @class Roo.bootstrap.TabPane
21734  * @extends Roo.bootstrap.Component
21735  * Bootstrap TabPane class
21736  * @cfg {Boolean} active (false | true) Default false
21737  * @cfg {String} title title of panel
21738
21739  * 
21740  * @constructor
21741  * Create a new TabPane
21742  * @param {Object} config The config object
21743  */
21744
21745 Roo.bootstrap.dash.TabPane = function(config){
21746     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21747     
21748     this.addEvents({
21749         // raw events
21750         /**
21751          * @event activate
21752          * When a pane is activated
21753          * @param {Roo.bootstrap.dash.TabPane} pane
21754          */
21755         "activate" : true
21756          
21757     });
21758 };
21759
21760 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21761     
21762     active : false,
21763     title : '',
21764     
21765     // the tabBox that this is attached to.
21766     tab : false,
21767      
21768     getAutoCreate : function() 
21769     {
21770         var cfg = {
21771             tag: 'div',
21772             cls: 'tab-pane'
21773         }
21774         
21775         if(this.active){
21776             cfg.cls += ' active';
21777         }
21778         
21779         return cfg;
21780     },
21781     initEvents  : function()
21782     {
21783         //Roo.log('trigger add pane handler');
21784         this.parent().fireEvent('addpane', this)
21785     },
21786     
21787      /**
21788      * Updates the tab title 
21789      * @param {String} html to set the title to.
21790      */
21791     setTitle: function(str)
21792     {
21793         if (!this.tab) {
21794             return;
21795         }
21796         this.title = str;
21797         this.tab.select('a', true).first().dom.innerHTML = str;
21798         
21799     }
21800     
21801     
21802     
21803 });
21804
21805  
21806
21807
21808  /*
21809  * - LGPL
21810  *
21811  * menu
21812  * 
21813  */
21814 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21815
21816 /**
21817  * @class Roo.bootstrap.menu.Menu
21818  * @extends Roo.bootstrap.Component
21819  * Bootstrap Menu class - container for Menu
21820  * @cfg {String} html Text of the menu
21821  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21822  * @cfg {String} icon Font awesome icon
21823  * @cfg {String} pos Menu align to (top | bottom) default bottom
21824  * 
21825  * 
21826  * @constructor
21827  * Create a new Menu
21828  * @param {Object} config The config object
21829  */
21830
21831
21832 Roo.bootstrap.menu.Menu = function(config){
21833     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21834     
21835     this.addEvents({
21836         /**
21837          * @event beforeshow
21838          * Fires before this menu is displayed
21839          * @param {Roo.bootstrap.menu.Menu} this
21840          */
21841         beforeshow : true,
21842         /**
21843          * @event beforehide
21844          * Fires before this menu is hidden
21845          * @param {Roo.bootstrap.menu.Menu} this
21846          */
21847         beforehide : true,
21848         /**
21849          * @event show
21850          * Fires after this menu is displayed
21851          * @param {Roo.bootstrap.menu.Menu} this
21852          */
21853         show : true,
21854         /**
21855          * @event hide
21856          * Fires after this menu is hidden
21857          * @param {Roo.bootstrap.menu.Menu} this
21858          */
21859         hide : true,
21860         /**
21861          * @event click
21862          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21863          * @param {Roo.bootstrap.menu.Menu} this
21864          * @param {Roo.EventObject} e
21865          */
21866         click : true
21867     });
21868     
21869 };
21870
21871 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21872     
21873     submenu : false,
21874     html : '',
21875     weight : 'default',
21876     icon : false,
21877     pos : 'bottom',
21878     
21879     
21880     getChildContainer : function() {
21881         if(this.isSubMenu){
21882             return this.el;
21883         }
21884         
21885         return this.el.select('ul.dropdown-menu', true).first();  
21886     },
21887     
21888     getAutoCreate : function()
21889     {
21890         var text = [
21891             {
21892                 tag : 'span',
21893                 cls : 'roo-menu-text',
21894                 html : this.html
21895             }
21896         ];
21897         
21898         if(this.icon){
21899             text.unshift({
21900                 tag : 'i',
21901                 cls : 'fa ' + this.icon
21902             })
21903         }
21904         
21905         
21906         var cfg = {
21907             tag : 'div',
21908             cls : 'btn-group',
21909             cn : [
21910                 {
21911                     tag : 'button',
21912                     cls : 'dropdown-button btn btn-' + this.weight,
21913                     cn : text
21914                 },
21915                 {
21916                     tag : 'button',
21917                     cls : 'dropdown-toggle btn btn-' + this.weight,
21918                     cn : [
21919                         {
21920                             tag : 'span',
21921                             cls : 'caret'
21922                         }
21923                     ]
21924                 },
21925                 {
21926                     tag : 'ul',
21927                     cls : 'dropdown-menu'
21928                 }
21929             ]
21930             
21931         };
21932         
21933         if(this.pos == 'top'){
21934             cfg.cls += ' dropup';
21935         }
21936         
21937         if(this.isSubMenu){
21938             cfg = {
21939                 tag : 'ul',
21940                 cls : 'dropdown-menu'
21941             }
21942         }
21943         
21944         return cfg;
21945     },
21946     
21947     onRender : function(ct, position)
21948     {
21949         this.isSubMenu = ct.hasClass('dropdown-submenu');
21950         
21951         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21952     },
21953     
21954     initEvents : function() 
21955     {
21956         if(this.isSubMenu){
21957             return;
21958         }
21959         
21960         this.hidden = true;
21961         
21962         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21963         this.triggerEl.on('click', this.onTriggerPress, this);
21964         
21965         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21966         this.buttonEl.on('click', this.onClick, this);
21967         
21968     },
21969     
21970     list : function()
21971     {
21972         if(this.isSubMenu){
21973             return this.el;
21974         }
21975         
21976         return this.el.select('ul.dropdown-menu', true).first();
21977     },
21978     
21979     onClick : function(e)
21980     {
21981         this.fireEvent("click", this, e);
21982     },
21983     
21984     onTriggerPress  : function(e)
21985     {   
21986         if (this.isVisible()) {
21987             this.hide();
21988         } else {
21989             this.show();
21990         }
21991     },
21992     
21993     isVisible : function(){
21994         return !this.hidden;
21995     },
21996     
21997     show : function()
21998     {
21999         this.fireEvent("beforeshow", this);
22000         
22001         this.hidden = false;
22002         this.el.addClass('open');
22003         
22004         Roo.get(document).on("mouseup", this.onMouseUp, this);
22005         
22006         this.fireEvent("show", this);
22007         
22008         
22009     },
22010     
22011     hide : function()
22012     {
22013         this.fireEvent("beforehide", this);
22014         
22015         this.hidden = true;
22016         this.el.removeClass('open');
22017         
22018         Roo.get(document).un("mouseup", this.onMouseUp);
22019         
22020         this.fireEvent("hide", this);
22021     },
22022     
22023     onMouseUp : function()
22024     {
22025         this.hide();
22026     }
22027     
22028 });
22029
22030  
22031  /*
22032  * - LGPL
22033  *
22034  * menu item
22035  * 
22036  */
22037 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22038
22039 /**
22040  * @class Roo.bootstrap.menu.Item
22041  * @extends Roo.bootstrap.Component
22042  * Bootstrap MenuItem class
22043  * @cfg {Boolean} submenu (true | false) default false
22044  * @cfg {String} html text of the item
22045  * @cfg {String} href the link
22046  * @cfg {Boolean} disable (true | false) default false
22047  * @cfg {Boolean} preventDefault (true | false) default true
22048  * @cfg {String} icon Font awesome icon
22049  * @cfg {String} pos Submenu align to (left | right) default right 
22050  * 
22051  * 
22052  * @constructor
22053  * Create a new Item
22054  * @param {Object} config The config object
22055  */
22056
22057
22058 Roo.bootstrap.menu.Item = function(config){
22059     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22060     this.addEvents({
22061         /**
22062          * @event mouseover
22063          * Fires when the mouse is hovering over this menu
22064          * @param {Roo.bootstrap.menu.Item} this
22065          * @param {Roo.EventObject} e
22066          */
22067         mouseover : true,
22068         /**
22069          * @event mouseout
22070          * Fires when the mouse exits this menu
22071          * @param {Roo.bootstrap.menu.Item} this
22072          * @param {Roo.EventObject} e
22073          */
22074         mouseout : true,
22075         // raw events
22076         /**
22077          * @event click
22078          * The raw click event for the entire grid.
22079          * @param {Roo.EventObject} e
22080          */
22081         click : true
22082     });
22083 };
22084
22085 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22086     
22087     submenu : false,
22088     href : '',
22089     html : '',
22090     preventDefault: true,
22091     disable : false,
22092     icon : false,
22093     pos : 'right',
22094     
22095     getAutoCreate : function()
22096     {
22097         var text = [
22098             {
22099                 tag : 'span',
22100                 cls : 'roo-menu-item-text',
22101                 html : this.html
22102             }
22103         ];
22104         
22105         if(this.icon){
22106             text.unshift({
22107                 tag : 'i',
22108                 cls : 'fa ' + this.icon
22109             })
22110         }
22111         
22112         var cfg = {
22113             tag : 'li',
22114             cn : [
22115                 {
22116                     tag : 'a',
22117                     href : this.href || '#',
22118                     cn : text
22119                 }
22120             ]
22121         };
22122         
22123         if(this.disable){
22124             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22125         }
22126         
22127         if(this.submenu){
22128             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22129             
22130             if(this.pos == 'left'){
22131                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22132             }
22133         }
22134         
22135         return cfg;
22136     },
22137     
22138     initEvents : function() 
22139     {
22140         this.el.on('mouseover', this.onMouseOver, this);
22141         this.el.on('mouseout', this.onMouseOut, this);
22142         
22143         this.el.select('a', true).first().on('click', this.onClick, this);
22144         
22145     },
22146     
22147     onClick : function(e)
22148     {
22149         if(this.preventDefault){
22150             e.preventDefault();
22151         }
22152         
22153         this.fireEvent("click", this, e);
22154     },
22155     
22156     onMouseOver : function(e)
22157     {
22158         if(this.submenu && this.pos == 'left'){
22159             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22160         }
22161         
22162         this.fireEvent("mouseover", this, e);
22163     },
22164     
22165     onMouseOut : function(e)
22166     {
22167         this.fireEvent("mouseout", this, e);
22168     }
22169 });
22170
22171  
22172
22173  /*
22174  * - LGPL
22175  *
22176  * menu separator
22177  * 
22178  */
22179 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22180
22181 /**
22182  * @class Roo.bootstrap.menu.Separator
22183  * @extends Roo.bootstrap.Component
22184  * Bootstrap Separator class
22185  * 
22186  * @constructor
22187  * Create a new Separator
22188  * @param {Object} config The config object
22189  */
22190
22191
22192 Roo.bootstrap.menu.Separator = function(config){
22193     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22194 };
22195
22196 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22197     
22198     getAutoCreate : function(){
22199         var cfg = {
22200             tag : 'li',
22201             cls: 'divider'
22202         };
22203         
22204         return cfg;
22205     }
22206    
22207 });
22208
22209  
22210
22211  /*
22212  * - LGPL
22213  *
22214  * Tooltip
22215  * 
22216  */
22217
22218 /**
22219  * @class Roo.bootstrap.Tooltip
22220  * Bootstrap Tooltip class
22221  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22222  * to determine which dom element triggers the tooltip.
22223  * 
22224  * It needs to add support for additional attributes like tooltip-position
22225  * 
22226  * @constructor
22227  * Create a new Toolti
22228  * @param {Object} config The config object
22229  */
22230
22231 Roo.bootstrap.Tooltip = function(config){
22232     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22233 };
22234
22235 Roo.apply(Roo.bootstrap.Tooltip, {
22236     /**
22237      * @function init initialize tooltip monitoring.
22238      * @static
22239      */
22240     currentEl : false,
22241     currentTip : false,
22242     currentRegion : false,
22243     
22244     //  init : delay?
22245     
22246     init : function()
22247     {
22248         Roo.get(document).on('mouseover', this.enter ,this);
22249         Roo.get(document).on('mouseout', this.leave, this);
22250          
22251         
22252         this.currentTip = new Roo.bootstrap.Tooltip();
22253     },
22254     
22255     enter : function(ev)
22256     {
22257         var dom = ev.getTarget();
22258         
22259         //Roo.log(['enter',dom]);
22260         var el = Roo.fly(dom);
22261         if (this.currentEl) {
22262             //Roo.log(dom);
22263             //Roo.log(this.currentEl);
22264             //Roo.log(this.currentEl.contains(dom));
22265             if (this.currentEl == el) {
22266                 return;
22267             }
22268             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22269                 return;
22270             }
22271
22272         }
22273         
22274         
22275         
22276         if (this.currentTip.el) {
22277             this.currentTip.el.hide(); // force hiding...
22278         }    
22279         //Roo.log(ev);
22280         var bindEl = el;
22281         
22282         // you can not look for children, as if el is the body.. then everythign is the child..
22283         if (!el.attr('tooltip')) { //
22284             if (!el.select("[tooltip]").elements.length) {
22285                 return;
22286             }
22287             // is the mouse over this child...?
22288             bindEl = el.select("[tooltip]").first();
22289             var xy = ev.getXY();
22290             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22291                 //Roo.log("not in region.");
22292                 return;
22293             }
22294             //Roo.log("child element over..");
22295             
22296         }
22297         this.currentEl = bindEl;
22298         this.currentTip.bind(bindEl);
22299         this.currentRegion = Roo.lib.Region.getRegion(dom);
22300         this.currentTip.enter();
22301         
22302     },
22303     leave : function(ev)
22304     {
22305         var dom = ev.getTarget();
22306         //Roo.log(['leave',dom]);
22307         if (!this.currentEl) {
22308             return;
22309         }
22310         
22311         
22312         if (dom != this.currentEl.dom) {
22313             return;
22314         }
22315         var xy = ev.getXY();
22316         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22317             return;
22318         }
22319         // only activate leave if mouse cursor is outside... bounding box..
22320         
22321         
22322         
22323         
22324         if (this.currentTip) {
22325             this.currentTip.leave();
22326         }
22327         //Roo.log('clear currentEl');
22328         this.currentEl = false;
22329         
22330         
22331     },
22332     alignment : {
22333         'left' : ['r-l', [-2,0], 'right'],
22334         'right' : ['l-r', [2,0], 'left'],
22335         'bottom' : ['t-b', [0,2], 'top'],
22336         'top' : [ 'b-t', [0,-2], 'bottom']
22337     }
22338     
22339 });
22340
22341
22342 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22343     
22344     
22345     bindEl : false,
22346     
22347     delay : null, // can be { show : 300 , hide: 500}
22348     
22349     timeout : null,
22350     
22351     hoverState : null, //???
22352     
22353     placement : 'bottom', 
22354     
22355     getAutoCreate : function(){
22356     
22357         var cfg = {
22358            cls : 'tooltip',
22359            role : 'tooltip',
22360            cn : [
22361                 {
22362                     cls : 'tooltip-arrow'
22363                 },
22364                 {
22365                     cls : 'tooltip-inner'
22366                 }
22367            ]
22368         };
22369         
22370         return cfg;
22371     },
22372     bind : function(el)
22373     {
22374         this.bindEl = el;
22375     },
22376       
22377     
22378     enter : function () {
22379        
22380         if (this.timeout != null) {
22381             clearTimeout(this.timeout);
22382         }
22383         
22384         this.hoverState = 'in';
22385          //Roo.log("enter - show");
22386         if (!this.delay || !this.delay.show) {
22387             this.show();
22388             return;
22389         }
22390         var _t = this;
22391         this.timeout = setTimeout(function () {
22392             if (_t.hoverState == 'in') {
22393                 _t.show();
22394             }
22395         }, this.delay.show);
22396     },
22397     leave : function()
22398     {
22399         clearTimeout(this.timeout);
22400     
22401         this.hoverState = 'out';
22402          if (!this.delay || !this.delay.hide) {
22403             this.hide();
22404             return;
22405         }
22406        
22407         var _t = this;
22408         this.timeout = setTimeout(function () {
22409             //Roo.log("leave - timeout");
22410             
22411             if (_t.hoverState == 'out') {
22412                 _t.hide();
22413                 Roo.bootstrap.Tooltip.currentEl = false;
22414             }
22415         }, delay);
22416     },
22417     
22418     show : function ()
22419     {
22420         if (!this.el) {
22421             this.render(document.body);
22422         }
22423         // set content.
22424         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22425         
22426         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22427         
22428         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22429         
22430         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22431         
22432         var placement = typeof this.placement == 'function' ?
22433             this.placement.call(this, this.el, on_el) :
22434             this.placement;
22435             
22436         var autoToken = /\s?auto?\s?/i;
22437         var autoPlace = autoToken.test(placement);
22438         if (autoPlace) {
22439             placement = placement.replace(autoToken, '') || 'top';
22440         }
22441         
22442         //this.el.detach()
22443         //this.el.setXY([0,0]);
22444         this.el.show();
22445         //this.el.dom.style.display='block';
22446         this.el.addClass(placement);
22447         
22448         //this.el.appendTo(on_el);
22449         
22450         var p = this.getPosition();
22451         var box = this.el.getBox();
22452         
22453         if (autoPlace) {
22454             // fixme..
22455         }
22456         var align = Roo.bootstrap.Tooltip.alignment[placement];
22457         this.el.alignTo(this.bindEl, align[0],align[1]);
22458         //var arrow = this.el.select('.arrow',true).first();
22459         //arrow.set(align[2], 
22460         
22461         this.el.addClass('in fade');
22462         this.hoverState = null;
22463         
22464         if (this.el.hasClass('fade')) {
22465             // fade it?
22466         }
22467         
22468     },
22469     hide : function()
22470     {
22471          
22472         if (!this.el) {
22473             return;
22474         }
22475         //this.el.setXY([0,0]);
22476         this.el.removeClass('in');
22477         //this.el.hide();
22478         
22479     }
22480     
22481 });
22482  
22483
22484  /*
22485  * - LGPL
22486  *
22487  * Location Picker
22488  * 
22489  */
22490
22491 /**
22492  * @class Roo.bootstrap.LocationPicker
22493  * @extends Roo.bootstrap.Component
22494  * Bootstrap LocationPicker class
22495  * @cfg {Number} latitude Position when init default 0
22496  * @cfg {Number} longitude Position when init default 0
22497  * @cfg {Number} zoom default 15
22498  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22499  * @cfg {Boolean} mapTypeControl default false
22500  * @cfg {Boolean} disableDoubleClickZoom default false
22501  * @cfg {Boolean} scrollwheel default true
22502  * @cfg {Boolean} streetViewControl default false
22503  * @cfg {Number} radius default 0
22504  * @cfg {String} locationName
22505  * @cfg {Boolean} draggable default true
22506  * @cfg {Boolean} enableAutocomplete default false
22507  * @cfg {Boolean} enableReverseGeocode default true
22508  * @cfg {String} markerTitle
22509  * 
22510  * @constructor
22511  * Create a new LocationPicker
22512  * @param {Object} config The config object
22513  */
22514
22515
22516 Roo.bootstrap.LocationPicker = function(config){
22517     
22518     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22519     
22520     this.addEvents({
22521         /**
22522          * @event initial
22523          * Fires when the picker initialized.
22524          * @param {Roo.bootstrap.LocationPicker} this
22525          * @param {Google Location} location
22526          */
22527         initial : true,
22528         /**
22529          * @event positionchanged
22530          * Fires when the picker position changed.
22531          * @param {Roo.bootstrap.LocationPicker} this
22532          * @param {Google Location} location
22533          */
22534         positionchanged : true,
22535         /**
22536          * @event resize
22537          * Fires when the map resize.
22538          * @param {Roo.bootstrap.LocationPicker} this
22539          */
22540         resize : true,
22541         /**
22542          * @event show
22543          * Fires when the map show.
22544          * @param {Roo.bootstrap.LocationPicker} this
22545          */
22546         show : true,
22547         /**
22548          * @event hide
22549          * Fires when the map hide.
22550          * @param {Roo.bootstrap.LocationPicker} this
22551          */
22552         hide : true,
22553         /**
22554          * @event mapClick
22555          * Fires when click the map.
22556          * @param {Roo.bootstrap.LocationPicker} this
22557          * @param {Map event} e
22558          */
22559         mapClick : true,
22560         /**
22561          * @event mapRightClick
22562          * Fires when right click the map.
22563          * @param {Roo.bootstrap.LocationPicker} this
22564          * @param {Map event} e
22565          */
22566         mapRightClick : true,
22567         /**
22568          * @event markerClick
22569          * Fires when click the marker.
22570          * @param {Roo.bootstrap.LocationPicker} this
22571          * @param {Map event} e
22572          */
22573         markerClick : true,
22574         /**
22575          * @event markerRightClick
22576          * Fires when right click the marker.
22577          * @param {Roo.bootstrap.LocationPicker} this
22578          * @param {Map event} e
22579          */
22580         markerRightClick : true,
22581         /**
22582          * @event OverlayViewDraw
22583          * Fires when OverlayView Draw
22584          * @param {Roo.bootstrap.LocationPicker} this
22585          */
22586         OverlayViewDraw : true,
22587         /**
22588          * @event OverlayViewOnAdd
22589          * Fires when OverlayView Draw
22590          * @param {Roo.bootstrap.LocationPicker} this
22591          */
22592         OverlayViewOnAdd : true,
22593         /**
22594          * @event OverlayViewOnRemove
22595          * Fires when OverlayView Draw
22596          * @param {Roo.bootstrap.LocationPicker} this
22597          */
22598         OverlayViewOnRemove : true,
22599         /**
22600          * @event OverlayViewShow
22601          * Fires when OverlayView Draw
22602          * @param {Roo.bootstrap.LocationPicker} this
22603          * @param {Pixel} cpx
22604          */
22605         OverlayViewShow : true,
22606         /**
22607          * @event OverlayViewHide
22608          * Fires when OverlayView Draw
22609          * @param {Roo.bootstrap.LocationPicker} this
22610          */
22611         OverlayViewHide : true
22612     });
22613         
22614 };
22615
22616 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22617     
22618     gMapContext: false,
22619     
22620     latitude: 0,
22621     longitude: 0,
22622     zoom: 15,
22623     mapTypeId: false,
22624     mapTypeControl: false,
22625     disableDoubleClickZoom: false,
22626     scrollwheel: true,
22627     streetViewControl: false,
22628     radius: 0,
22629     locationName: '',
22630     draggable: true,
22631     enableAutocomplete: false,
22632     enableReverseGeocode: true,
22633     markerTitle: '',
22634     
22635     getAutoCreate: function()
22636     {
22637
22638         var cfg = {
22639             tag: 'div',
22640             cls: 'roo-location-picker'
22641         };
22642         
22643         return cfg
22644     },
22645     
22646     initEvents: function(ct, position)
22647     {       
22648         if(!this.el.getWidth() || this.isApplied()){
22649             return;
22650         }
22651         
22652         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22653         
22654         this.initial();
22655     },
22656     
22657     initial: function()
22658     {
22659         if(!this.mapTypeId){
22660             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22661         }
22662         
22663         this.gMapContext = this.GMapContext();
22664         
22665         this.initOverlayView();
22666         
22667         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22668         
22669         var _this = this;
22670                 
22671         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22672             _this.setPosition(_this.gMapContext.marker.position);
22673         });
22674         
22675         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22676             _this.fireEvent('mapClick', this, event);
22677             
22678         });
22679
22680         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22681             _this.fireEvent('mapRightClick', this, event);
22682             
22683         });
22684         
22685         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22686             _this.fireEvent('markerClick', this, event);
22687             
22688         });
22689
22690         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22691             _this.fireEvent('markerRightClick', this, event);
22692             
22693         });
22694         
22695         this.setPosition(this.gMapContext.location);
22696         
22697         this.fireEvent('initial', this, this.gMapContext.location);
22698     },
22699     
22700     initOverlayView: function()
22701     {
22702         var _this = this;
22703         
22704         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22705             
22706             draw: function()
22707             {
22708                 _this.fireEvent('OverlayViewDraw', _this);
22709             },
22710             
22711             onAdd: function()
22712             {
22713                 _this.fireEvent('OverlayViewOnAdd', _this);
22714             },
22715             
22716             onRemove: function()
22717             {
22718                 _this.fireEvent('OverlayViewOnRemove', _this);
22719             },
22720             
22721             show: function(cpx)
22722             {
22723                 _this.fireEvent('OverlayViewShow', _this, cpx);
22724             },
22725             
22726             hide: function()
22727             {
22728                 _this.fireEvent('OverlayViewHide', _this);
22729             }
22730             
22731         });
22732     },
22733     
22734     fromLatLngToContainerPixel: function(event)
22735     {
22736         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22737     },
22738     
22739     isApplied: function() 
22740     {
22741         return this.getGmapContext() == false ? false : true;
22742     },
22743     
22744     getGmapContext: function() 
22745     {
22746         return this.gMapContext
22747     },
22748     
22749     GMapContext: function() 
22750     {
22751         var position = new google.maps.LatLng(this.latitude, this.longitude);
22752         
22753         var _map = new google.maps.Map(this.el.dom, {
22754             center: position,
22755             zoom: this.zoom,
22756             mapTypeId: this.mapTypeId,
22757             mapTypeControl: this.mapTypeControl,
22758             disableDoubleClickZoom: this.disableDoubleClickZoom,
22759             scrollwheel: this.scrollwheel,
22760             streetViewControl: this.streetViewControl,
22761             locationName: this.locationName,
22762             draggable: this.draggable,
22763             enableAutocomplete: this.enableAutocomplete,
22764             enableReverseGeocode: this.enableReverseGeocode
22765         });
22766         
22767         var _marker = new google.maps.Marker({
22768             position: position,
22769             map: _map,
22770             title: this.markerTitle,
22771             draggable: this.draggable
22772         });
22773         
22774         return {
22775             map: _map,
22776             marker: _marker,
22777             circle: null,
22778             location: position,
22779             radius: this.radius,
22780             locationName: this.locationName,
22781             addressComponents: {
22782                 formatted_address: null,
22783                 addressLine1: null,
22784                 addressLine2: null,
22785                 streetName: null,
22786                 streetNumber: null,
22787                 city: null,
22788                 district: null,
22789                 state: null,
22790                 stateOrProvince: null
22791             },
22792             settings: this,
22793             domContainer: this.el.dom,
22794             geodecoder: new google.maps.Geocoder()
22795         };
22796     },
22797     
22798     drawCircle: function(center, radius, options) 
22799     {
22800         if (this.gMapContext.circle != null) {
22801             this.gMapContext.circle.setMap(null);
22802         }
22803         if (radius > 0) {
22804             radius *= 1;
22805             options = Roo.apply({}, options, {
22806                 strokeColor: "#0000FF",
22807                 strokeOpacity: .35,
22808                 strokeWeight: 2,
22809                 fillColor: "#0000FF",
22810                 fillOpacity: .2
22811             });
22812             
22813             options.map = this.gMapContext.map;
22814             options.radius = radius;
22815             options.center = center;
22816             this.gMapContext.circle = new google.maps.Circle(options);
22817             return this.gMapContext.circle;
22818         }
22819         
22820         return null;
22821     },
22822     
22823     setPosition: function(location) 
22824     {
22825         this.gMapContext.location = location;
22826         this.gMapContext.marker.setPosition(location);
22827         this.gMapContext.map.panTo(location);
22828         this.drawCircle(location, this.gMapContext.radius, {});
22829         
22830         var _this = this;
22831         
22832         if (this.gMapContext.settings.enableReverseGeocode) {
22833             this.gMapContext.geodecoder.geocode({
22834                 latLng: this.gMapContext.location
22835             }, function(results, status) {
22836                 
22837                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22838                     _this.gMapContext.locationName = results[0].formatted_address;
22839                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22840                     
22841                     _this.fireEvent('positionchanged', this, location);
22842                 }
22843             });
22844             
22845             return;
22846         }
22847         
22848         this.fireEvent('positionchanged', this, location);
22849     },
22850     
22851     resize: function()
22852     {
22853         google.maps.event.trigger(this.gMapContext.map, "resize");
22854         
22855         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22856         
22857         this.fireEvent('resize', this);
22858     },
22859     
22860     setPositionByLatLng: function(latitude, longitude)
22861     {
22862         this.setPosition(new google.maps.LatLng(latitude, longitude));
22863     },
22864     
22865     getCurrentPosition: function() 
22866     {
22867         return {
22868             latitude: this.gMapContext.location.lat(),
22869             longitude: this.gMapContext.location.lng()
22870         };
22871     },
22872     
22873     getAddressName: function() 
22874     {
22875         return this.gMapContext.locationName;
22876     },
22877     
22878     getAddressComponents: function() 
22879     {
22880         return this.gMapContext.addressComponents;
22881     },
22882     
22883     address_component_from_google_geocode: function(address_components) 
22884     {
22885         var result = {};
22886         
22887         for (var i = 0; i < address_components.length; i++) {
22888             var component = address_components[i];
22889             if (component.types.indexOf("postal_code") >= 0) {
22890                 result.postalCode = component.short_name;
22891             } else if (component.types.indexOf("street_number") >= 0) {
22892                 result.streetNumber = component.short_name;
22893             } else if (component.types.indexOf("route") >= 0) {
22894                 result.streetName = component.short_name;
22895             } else if (component.types.indexOf("neighborhood") >= 0) {
22896                 result.city = component.short_name;
22897             } else if (component.types.indexOf("locality") >= 0) {
22898                 result.city = component.short_name;
22899             } else if (component.types.indexOf("sublocality") >= 0) {
22900                 result.district = component.short_name;
22901             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22902                 result.stateOrProvince = component.short_name;
22903             } else if (component.types.indexOf("country") >= 0) {
22904                 result.country = component.short_name;
22905             }
22906         }
22907         
22908         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22909         result.addressLine2 = "";
22910         return result;
22911     },
22912     
22913     setZoomLevel: function(zoom)
22914     {
22915         this.gMapContext.map.setZoom(zoom);
22916     },
22917     
22918     show: function()
22919     {
22920         if(!this.el){
22921             return;
22922         }
22923         
22924         this.el.show();
22925         
22926         this.resize();
22927         
22928         this.fireEvent('show', this);
22929     },
22930     
22931     hide: function()
22932     {
22933         if(!this.el){
22934             return;
22935         }
22936         
22937         this.el.hide();
22938         
22939         this.fireEvent('hide', this);
22940     }
22941     
22942 });
22943
22944 Roo.apply(Roo.bootstrap.LocationPicker, {
22945     
22946     OverlayView : function(map, options)
22947     {
22948         options = options || {};
22949         
22950         this.setMap(map);
22951     }
22952     
22953     
22954 });/*
22955  * - LGPL
22956  *
22957  * Alert
22958  * 
22959  */
22960
22961 /**
22962  * @class Roo.bootstrap.Alert
22963  * @extends Roo.bootstrap.Component
22964  * Bootstrap Alert class
22965  * @cfg {String} title The title of alert
22966  * @cfg {String} html The content of alert
22967  * @cfg {String} weight (  success | info | warning | danger )
22968  * @cfg {String} faicon font-awesomeicon
22969  * 
22970  * @constructor
22971  * Create a new alert
22972  * @param {Object} config The config object
22973  */
22974
22975
22976 Roo.bootstrap.Alert = function(config){
22977     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22978     
22979 };
22980
22981 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22982     
22983     title: '',
22984     html: '',
22985     weight: false,
22986     faicon: false,
22987     
22988     getAutoCreate : function()
22989     {
22990         
22991         var cfg = {
22992             tag : 'div',
22993             cls : 'alert',
22994             cn : [
22995                 {
22996                     tag : 'i',
22997                     cls : 'roo-alert-icon'
22998                     
22999                 },
23000                 {
23001                     tag : 'b',
23002                     cls : 'roo-alert-title',
23003                     html : this.title
23004                 },
23005                 {
23006                     tag : 'span',
23007                     cls : 'roo-alert-text',
23008                     html : this.html
23009                 }
23010             ]
23011         };
23012         
23013         if(this.faicon){
23014             cfg.cn[0].cls += ' fa ' + this.faicon;
23015         }
23016         
23017         if(this.weight){
23018             cfg.cls += ' alert-' + this.weight;
23019         }
23020         
23021         return cfg;
23022     },
23023     
23024     initEvents: function() 
23025     {
23026         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23027     },
23028     
23029     setTitle : function(str)
23030     {
23031         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23032     },
23033     
23034     setText : function(str)
23035     {
23036         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23037     },
23038     
23039     setWeight : function(weight)
23040     {
23041         if(this.weight){
23042             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23043         }
23044         
23045         this.weight = weight;
23046         
23047         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23048     },
23049     
23050     setIcon : function(icon)
23051     {
23052         if(this.faicon){
23053             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23054         }
23055         
23056         this.faicon = icon
23057         
23058         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23059     },
23060     
23061     hide: function() 
23062     {
23063         this.el.hide();   
23064     },
23065     
23066     show: function() 
23067     {  
23068         this.el.show();   
23069     }
23070     
23071 });
23072
23073