roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         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     
4064     isActive: function () {
4065         return this.active
4066     },
4067     setActive : function(state, fire, is_was_active)
4068     {
4069         if (this.active && !state & this.navId) {
4070             this.was_active = true;
4071             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4072             if (nv) {
4073                 nv.clearWasActive(this);
4074             }
4075             
4076         }
4077         this.active = state;
4078         
4079         if (!state ) {
4080             this.el.removeClass('active');
4081         } else if (!this.el.hasClass('active')) {
4082             this.el.addClass('active');
4083         }
4084         if (fire) {
4085             this.fireEvent('changed', this, state);
4086         }
4087         
4088         // show a panel if it's registered and related..
4089         
4090         if (!this.navId || !this.tabId || !state || is_was_active) {
4091             return;
4092         }
4093         
4094         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4095         if (!tg) {
4096             return;
4097         }
4098         var pan = tg.getPanelByName(this.tabId);
4099         if (!pan) {
4100             return;
4101         }
4102         // if we can not flip to new panel - go back to old nav highlight..
4103         if (false == tg.showPanel(pan)) {
4104             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4105             if (nv) {
4106                 var onav = nv.getWasActive();
4107                 if (onav) {
4108                     onav.setActive(true, false, true);
4109                 }
4110             }
4111             
4112         }
4113         
4114         
4115         
4116     },
4117      // this should not be here...
4118     setDisabled : function(state)
4119     {
4120         this.disabled = state;
4121         if (!state ) {
4122             this.el.removeClass('disabled');
4123         } else if (!this.el.hasClass('disabled')) {
4124             this.el.addClass('disabled');
4125         }
4126         
4127     },
4128     
4129     /**
4130      * Fetch the element to display the tooltip on.
4131      * @return {Roo.Element} defaults to this.el
4132      */
4133     tooltipEl : function()
4134     {
4135         return this.el.select('' + this.tagtype + '', true).first();
4136     },
4137     
4138     scrollToElement : function(e)
4139     {
4140         var c = document.body;
4141         
4142         /*
4143          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4144          */
4145         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4146             c = document.documentElement;
4147         }
4148         
4149         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4150         
4151         if(!target){
4152             return;
4153         }
4154
4155         var o = target.calcOffsetsTo(c);
4156         
4157         var options = {
4158             target : target,
4159             value : o[1]
4160         }
4161         
4162         this.fireEvent('scrollto', this, options, e);
4163         
4164         Roo.get(c).scrollTo('top', options.value, true);
4165         
4166         return;
4167     }
4168 });
4169  
4170
4171  /*
4172  * - LGPL
4173  *
4174  * sidebar item
4175  *
4176  *  li
4177  *    <span> icon </span>
4178  *    <span> text </span>
4179  *    <span>badge </span>
4180  */
4181
4182 /**
4183  * @class Roo.bootstrap.NavSidebarItem
4184  * @extends Roo.bootstrap.NavItem
4185  * Bootstrap Navbar.NavSidebarItem class
4186  * @constructor
4187  * Create a new Navbar Button
4188  * @param {Object} config The config object
4189  */
4190 Roo.bootstrap.NavSidebarItem = function(config){
4191     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4192     this.addEvents({
4193         // raw events
4194         /**
4195          * @event click
4196          * The raw click event for the entire grid.
4197          * @param {Roo.EventObject} e
4198          */
4199         "click" : true,
4200          /**
4201             * @event changed
4202             * Fires when the active item active state changes
4203             * @param {Roo.bootstrap.NavSidebarItem} this
4204             * @param {boolean} state the new state
4205              
4206          */
4207         'changed': true
4208     });
4209    
4210 };
4211
4212 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4213     
4214     
4215     getAutoCreate : function(){
4216         
4217         
4218         var a = {
4219                 tag: 'a',
4220                 href : this.href || '#',
4221                 cls: '',
4222                 html : '',
4223                 cn : []
4224         };
4225         var cfg = {
4226             tag: 'li',
4227             cls: '',
4228             cn: [ a ]
4229         }
4230         var span = {
4231             tag: 'span',
4232             html : this.html || ''
4233         }
4234         
4235         
4236         if (this.active) {
4237             cfg.cls += ' active';
4238         }
4239         
4240         // left icon..
4241         if (this.glyphicon || this.icon) {
4242             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4243             a.cn.push({ tag : 'i', cls : c }) ;
4244         }
4245         // html..
4246         a.cn.push(span);
4247         // then badge..
4248         if (this.badge !== '') {
4249             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4250         }
4251         // fi
4252         if (this.menu) {
4253             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4254             a.cls += 'dropdown-toggle treeview' ;
4255             
4256         }
4257         
4258         
4259         
4260         return cfg;
4261          
4262            
4263     }
4264    
4265      
4266  
4267 });
4268  
4269
4270  /*
4271  * - LGPL
4272  *
4273  * row
4274  * 
4275  */
4276
4277 /**
4278  * @class Roo.bootstrap.Row
4279  * @extends Roo.bootstrap.Component
4280  * Bootstrap Row class (contains columns...)
4281  * 
4282  * @constructor
4283  * Create a new Row
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Row = function(config){
4288     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4289 };
4290
4291 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4292     
4293     getAutoCreate : function(){
4294        return {
4295             cls: 'row clearfix'
4296        };
4297     }
4298     
4299     
4300 });
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * element
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.Element
4313  * @extends Roo.bootstrap.Component
4314  * Bootstrap Element class
4315  * @cfg {String} html contents of the element
4316  * @cfg {String} tag tag of the element
4317  * @cfg {String} cls class of the element
4318  * @cfg {Boolean} preventDefault (true|false) default false
4319  * @cfg {Boolean} clickable (true|false) default false
4320  * 
4321  * @constructor
4322  * Create a new Element
4323  * @param {Object} config The config object
4324  */
4325
4326 Roo.bootstrap.Element = function(config){
4327     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4328     
4329     this.addEvents({
4330         // raw events
4331         /**
4332          * @event click
4333          * When a element is chick
4334          * @param {Roo.bootstrap.Element} this
4335          * @param {Roo.EventObject} e
4336          */
4337         "click" : true
4338     });
4339 };
4340
4341 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4342     
4343     tag: 'div',
4344     cls: '',
4345     html: '',
4346     preventDefault: false, 
4347     clickable: false,
4348     
4349     getAutoCreate : function(){
4350         
4351         var cfg = {
4352             tag: this.tag,
4353             cls: this.cls,
4354             html: this.html
4355         }
4356         
4357         return cfg;
4358     },
4359     
4360     initEvents: function() 
4361     {
4362         Roo.bootstrap.Element.superclass.initEvents.call(this);
4363         
4364         if(this.clickable){
4365             this.el.on('click', this.onClick, this);
4366         }
4367         
4368     },
4369     
4370     onClick : function(e)
4371     {
4372         if(this.preventDefault){
4373             e.preventDefault();
4374         }
4375         
4376         this.fireEvent('click', this, e);
4377     },
4378     
4379     getValue : function()
4380     {
4381         return this.el.dom.innerHTML;
4382     },
4383     
4384     setValue : function(value)
4385     {
4386         this.el.dom.innerHTML = value;
4387     }
4388    
4389 });
4390
4391  
4392
4393  /*
4394  * - LGPL
4395  *
4396  * pagination
4397  * 
4398  */
4399
4400 /**
4401  * @class Roo.bootstrap.Pagination
4402  * @extends Roo.bootstrap.Component
4403  * Bootstrap Pagination class
4404  * @cfg {String} size xs | sm | md | lg
4405  * @cfg {Boolean} inverse false | true
4406  * 
4407  * @constructor
4408  * Create a new Pagination
4409  * @param {Object} config The config object
4410  */
4411
4412 Roo.bootstrap.Pagination = function(config){
4413     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4414 };
4415
4416 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4417     
4418     cls: false,
4419     size: false,
4420     inverse: false,
4421     
4422     getAutoCreate : function(){
4423         var cfg = {
4424             tag: 'ul',
4425                 cls: 'pagination'
4426         };
4427         if (this.inverse) {
4428             cfg.cls += ' inverse';
4429         }
4430         if (this.html) {
4431             cfg.html=this.html;
4432         }
4433         if (this.cls) {
4434             cfg.cls += " " + this.cls;
4435         }
4436         return cfg;
4437     }
4438    
4439 });
4440
4441  
4442
4443  /*
4444  * - LGPL
4445  *
4446  * Pagination item
4447  * 
4448  */
4449
4450
4451 /**
4452  * @class Roo.bootstrap.PaginationItem
4453  * @extends Roo.bootstrap.Component
4454  * Bootstrap PaginationItem class
4455  * @cfg {String} html text
4456  * @cfg {String} href the link
4457  * @cfg {Boolean} preventDefault (true | false) default true
4458  * @cfg {Boolean} active (true | false) default false
4459  * @cfg {Boolean} disabled default false
4460  * 
4461  * 
4462  * @constructor
4463  * Create a new PaginationItem
4464  * @param {Object} config The config object
4465  */
4466
4467
4468 Roo.bootstrap.PaginationItem = function(config){
4469     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4470     this.addEvents({
4471         // raw events
4472         /**
4473          * @event click
4474          * The raw click event for the entire grid.
4475          * @param {Roo.EventObject} e
4476          */
4477         "click" : true
4478     });
4479 };
4480
4481 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4482     
4483     href : false,
4484     html : false,
4485     preventDefault: true,
4486     active : false,
4487     cls : false,
4488     disabled: false,
4489     
4490     getAutoCreate : function(){
4491         var cfg= {
4492             tag: 'li',
4493             cn: [
4494                 {
4495                     tag : 'a',
4496                     href : this.href ? this.href : '#',
4497                     html : this.html ? this.html : ''
4498                 }
4499             ]
4500         };
4501         
4502         if(this.cls){
4503             cfg.cls = this.cls;
4504         }
4505         
4506         if(this.disabled){
4507             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4508         }
4509         
4510         if(this.active){
4511             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4512         }
4513         
4514         return cfg;
4515     },
4516     
4517     initEvents: function() {
4518         
4519         this.el.on('click', this.onClick, this);
4520         
4521     },
4522     onClick : function(e)
4523     {
4524         Roo.log('PaginationItem on click ');
4525         if(this.preventDefault){
4526             e.preventDefault();
4527         }
4528         
4529         if(this.disabled){
4530             return;
4531         }
4532         
4533         this.fireEvent('click', this, e);
4534     }
4535    
4536 });
4537
4538  
4539
4540  /*
4541  * - LGPL
4542  *
4543  * slider
4544  * 
4545  */
4546
4547
4548 /**
4549  * @class Roo.bootstrap.Slider
4550  * @extends Roo.bootstrap.Component
4551  * Bootstrap Slider class
4552  *    
4553  * @constructor
4554  * Create a new Slider
4555  * @param {Object} config The config object
4556  */
4557
4558 Roo.bootstrap.Slider = function(config){
4559     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4560 };
4561
4562 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4563     
4564     getAutoCreate : function(){
4565         
4566         var cfg = {
4567             tag: 'div',
4568             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4569             cn: [
4570                 {
4571                     tag: 'a',
4572                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4573                 }
4574             ]
4575         }
4576         
4577         return cfg;
4578     }
4579    
4580 });
4581
4582  /*
4583  * Based on:
4584  * Ext JS Library 1.1.1
4585  * Copyright(c) 2006-2007, Ext JS, LLC.
4586  *
4587  * Originally Released Under LGPL - original licence link has changed is not relivant.
4588  *
4589  * Fork - LGPL
4590  * <script type="text/javascript">
4591  */
4592  
4593
4594 /**
4595  * @class Roo.grid.ColumnModel
4596  * @extends Roo.util.Observable
4597  * This is the default implementation of a ColumnModel used by the Grid. It defines
4598  * the columns in the grid.
4599  * <br>Usage:<br>
4600  <pre><code>
4601  var colModel = new Roo.grid.ColumnModel([
4602         {header: "Ticker", width: 60, sortable: true, locked: true},
4603         {header: "Company Name", width: 150, sortable: true},
4604         {header: "Market Cap.", width: 100, sortable: true},
4605         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4606         {header: "Employees", width: 100, sortable: true, resizable: false}
4607  ]);
4608  </code></pre>
4609  * <p>
4610  
4611  * The config options listed for this class are options which may appear in each
4612  * individual column definition.
4613  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4614  * @constructor
4615  * @param {Object} config An Array of column config objects. See this class's
4616  * config objects for details.
4617 */
4618 Roo.grid.ColumnModel = function(config){
4619         /**
4620      * The config passed into the constructor
4621      */
4622     this.config = config;
4623     this.lookup = {};
4624
4625     // if no id, create one
4626     // if the column does not have a dataIndex mapping,
4627     // map it to the order it is in the config
4628     for(var i = 0, len = config.length; i < len; i++){
4629         var c = config[i];
4630         if(typeof c.dataIndex == "undefined"){
4631             c.dataIndex = i;
4632         }
4633         if(typeof c.renderer == "string"){
4634             c.renderer = Roo.util.Format[c.renderer];
4635         }
4636         if(typeof c.id == "undefined"){
4637             c.id = Roo.id();
4638         }
4639         if(c.editor && c.editor.xtype){
4640             c.editor  = Roo.factory(c.editor, Roo.grid);
4641         }
4642         if(c.editor && c.editor.isFormField){
4643             c.editor = new Roo.grid.GridEditor(c.editor);
4644         }
4645         this.lookup[c.id] = c;
4646     }
4647
4648     /**
4649      * The width of columns which have no width specified (defaults to 100)
4650      * @type Number
4651      */
4652     this.defaultWidth = 100;
4653
4654     /**
4655      * Default sortable of columns which have no sortable specified (defaults to false)
4656      * @type Boolean
4657      */
4658     this.defaultSortable = false;
4659
4660     this.addEvents({
4661         /**
4662              * @event widthchange
4663              * Fires when the width of a column changes.
4664              * @param {ColumnModel} this
4665              * @param {Number} columnIndex The column index
4666              * @param {Number} newWidth The new width
4667              */
4668             "widthchange": true,
4669         /**
4670              * @event headerchange
4671              * Fires when the text of a header changes.
4672              * @param {ColumnModel} this
4673              * @param {Number} columnIndex The column index
4674              * @param {Number} newText The new header text
4675              */
4676             "headerchange": true,
4677         /**
4678              * @event hiddenchange
4679              * Fires when a column is hidden or "unhidden".
4680              * @param {ColumnModel} this
4681              * @param {Number} columnIndex The column index
4682              * @param {Boolean} hidden true if hidden, false otherwise
4683              */
4684             "hiddenchange": true,
4685             /**
4686          * @event columnmoved
4687          * Fires when a column is moved.
4688          * @param {ColumnModel} this
4689          * @param {Number} oldIndex
4690          * @param {Number} newIndex
4691          */
4692         "columnmoved" : true,
4693         /**
4694          * @event columlockchange
4695          * Fires when a column's locked state is changed
4696          * @param {ColumnModel} this
4697          * @param {Number} colIndex
4698          * @param {Boolean} locked true if locked
4699          */
4700         "columnlockchange" : true
4701     });
4702     Roo.grid.ColumnModel.superclass.constructor.call(this);
4703 };
4704 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4705     /**
4706      * @cfg {String} header The header text to display in the Grid view.
4707      */
4708     /**
4709      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4710      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4711      * specified, the column's index is used as an index into the Record's data Array.
4712      */
4713     /**
4714      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4715      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4716      */
4717     /**
4718      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4719      * Defaults to the value of the {@link #defaultSortable} property.
4720      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4721      */
4722     /**
4723      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4724      */
4725     /**
4726      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4727      */
4728     /**
4729      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4730      */
4731     /**
4732      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4733      */
4734     /**
4735      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4736      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4737      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4738      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4739      */
4740        /**
4741      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4742      */
4743     /**
4744      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4745      */
4746     /**
4747      * @cfg {String} cursor (Optional)
4748      */
4749     /**
4750      * @cfg {String} tooltip (Optional)
4751      */
4752     /**
4753      * Returns the id of the column at the specified index.
4754      * @param {Number} index The column index
4755      * @return {String} the id
4756      */
4757     getColumnId : function(index){
4758         return this.config[index].id;
4759     },
4760
4761     /**
4762      * Returns the column for a specified id.
4763      * @param {String} id The column id
4764      * @return {Object} the column
4765      */
4766     getColumnById : function(id){
4767         return this.lookup[id];
4768     },
4769
4770     
4771     /**
4772      * Returns the column for a specified dataIndex.
4773      * @param {String} dataIndex The column dataIndex
4774      * @return {Object|Boolean} the column or false if not found
4775      */
4776     getColumnByDataIndex: function(dataIndex){
4777         var index = this.findColumnIndex(dataIndex);
4778         return index > -1 ? this.config[index] : false;
4779     },
4780     
4781     /**
4782      * Returns the index for a specified column id.
4783      * @param {String} id The column id
4784      * @return {Number} the index, or -1 if not found
4785      */
4786     getIndexById : function(id){
4787         for(var i = 0, len = this.config.length; i < len; i++){
4788             if(this.config[i].id == id){
4789                 return i;
4790             }
4791         }
4792         return -1;
4793     },
4794     
4795     /**
4796      * Returns the index for a specified column dataIndex.
4797      * @param {String} dataIndex The column dataIndex
4798      * @return {Number} the index, or -1 if not found
4799      */
4800     
4801     findColumnIndex : function(dataIndex){
4802         for(var i = 0, len = this.config.length; i < len; i++){
4803             if(this.config[i].dataIndex == dataIndex){
4804                 return i;
4805             }
4806         }
4807         return -1;
4808     },
4809     
4810     
4811     moveColumn : function(oldIndex, newIndex){
4812         var c = this.config[oldIndex];
4813         this.config.splice(oldIndex, 1);
4814         this.config.splice(newIndex, 0, c);
4815         this.dataMap = null;
4816         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4817     },
4818
4819     isLocked : function(colIndex){
4820         return this.config[colIndex].locked === true;
4821     },
4822
4823     setLocked : function(colIndex, value, suppressEvent){
4824         if(this.isLocked(colIndex) == value){
4825             return;
4826         }
4827         this.config[colIndex].locked = value;
4828         if(!suppressEvent){
4829             this.fireEvent("columnlockchange", this, colIndex, value);
4830         }
4831     },
4832
4833     getTotalLockedWidth : function(){
4834         var totalWidth = 0;
4835         for(var i = 0; i < this.config.length; i++){
4836             if(this.isLocked(i) && !this.isHidden(i)){
4837                 this.totalWidth += this.getColumnWidth(i);
4838             }
4839         }
4840         return totalWidth;
4841     },
4842
4843     getLockedCount : function(){
4844         for(var i = 0, len = this.config.length; i < len; i++){
4845             if(!this.isLocked(i)){
4846                 return i;
4847             }
4848         }
4849     },
4850
4851     /**
4852      * Returns the number of columns.
4853      * @return {Number}
4854      */
4855     getColumnCount : function(visibleOnly){
4856         if(visibleOnly === true){
4857             var c = 0;
4858             for(var i = 0, len = this.config.length; i < len; i++){
4859                 if(!this.isHidden(i)){
4860                     c++;
4861                 }
4862             }
4863             return c;
4864         }
4865         return this.config.length;
4866     },
4867
4868     /**
4869      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4870      * @param {Function} fn
4871      * @param {Object} scope (optional)
4872      * @return {Array} result
4873      */
4874     getColumnsBy : function(fn, scope){
4875         var r = [];
4876         for(var i = 0, len = this.config.length; i < len; i++){
4877             var c = this.config[i];
4878             if(fn.call(scope||this, c, i) === true){
4879                 r[r.length] = c;
4880             }
4881         }
4882         return r;
4883     },
4884
4885     /**
4886      * Returns true if the specified column is sortable.
4887      * @param {Number} col The column index
4888      * @return {Boolean}
4889      */
4890     isSortable : function(col){
4891         if(typeof this.config[col].sortable == "undefined"){
4892             return this.defaultSortable;
4893         }
4894         return this.config[col].sortable;
4895     },
4896
4897     /**
4898      * Returns the rendering (formatting) function defined for the column.
4899      * @param {Number} col The column index.
4900      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4901      */
4902     getRenderer : function(col){
4903         if(!this.config[col].renderer){
4904             return Roo.grid.ColumnModel.defaultRenderer;
4905         }
4906         return this.config[col].renderer;
4907     },
4908
4909     /**
4910      * Sets the rendering (formatting) function for a column.
4911      * @param {Number} col The column index
4912      * @param {Function} fn The function to use to process the cell's raw data
4913      * to return HTML markup for the grid view. The render function is called with
4914      * the following parameters:<ul>
4915      * <li>Data value.</li>
4916      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4917      * <li>css A CSS style string to apply to the table cell.</li>
4918      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4919      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4920      * <li>Row index</li>
4921      * <li>Column index</li>
4922      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4923      */
4924     setRenderer : function(col, fn){
4925         this.config[col].renderer = fn;
4926     },
4927
4928     /**
4929      * Returns the width for the specified column.
4930      * @param {Number} col The column index
4931      * @return {Number}
4932      */
4933     getColumnWidth : function(col){
4934         return this.config[col].width * 1 || this.defaultWidth;
4935     },
4936
4937     /**
4938      * Sets the width for a column.
4939      * @param {Number} col The column index
4940      * @param {Number} width The new width
4941      */
4942     setColumnWidth : function(col, width, suppressEvent){
4943         this.config[col].width = width;
4944         this.totalWidth = null;
4945         if(!suppressEvent){
4946              this.fireEvent("widthchange", this, col, width);
4947         }
4948     },
4949
4950     /**
4951      * Returns the total width of all columns.
4952      * @param {Boolean} includeHidden True to include hidden column widths
4953      * @return {Number}
4954      */
4955     getTotalWidth : function(includeHidden){
4956         if(!this.totalWidth){
4957             this.totalWidth = 0;
4958             for(var i = 0, len = this.config.length; i < len; i++){
4959                 if(includeHidden || !this.isHidden(i)){
4960                     this.totalWidth += this.getColumnWidth(i);
4961                 }
4962             }
4963         }
4964         return this.totalWidth;
4965     },
4966
4967     /**
4968      * Returns the header for the specified column.
4969      * @param {Number} col The column index
4970      * @return {String}
4971      */
4972     getColumnHeader : function(col){
4973         return this.config[col].header;
4974     },
4975
4976     /**
4977      * Sets the header for a column.
4978      * @param {Number} col The column index
4979      * @param {String} header The new header
4980      */
4981     setColumnHeader : function(col, header){
4982         this.config[col].header = header;
4983         this.fireEvent("headerchange", this, col, header);
4984     },
4985
4986     /**
4987      * Returns the tooltip for the specified column.
4988      * @param {Number} col The column index
4989      * @return {String}
4990      */
4991     getColumnTooltip : function(col){
4992             return this.config[col].tooltip;
4993     },
4994     /**
4995      * Sets the tooltip for a column.
4996      * @param {Number} col The column index
4997      * @param {String} tooltip The new tooltip
4998      */
4999     setColumnTooltip : function(col, tooltip){
5000             this.config[col].tooltip = tooltip;
5001     },
5002
5003     /**
5004      * Returns the dataIndex for the specified column.
5005      * @param {Number} col The column index
5006      * @return {Number}
5007      */
5008     getDataIndex : function(col){
5009         return this.config[col].dataIndex;
5010     },
5011
5012     /**
5013      * Sets the dataIndex for a column.
5014      * @param {Number} col The column index
5015      * @param {Number} dataIndex The new dataIndex
5016      */
5017     setDataIndex : function(col, dataIndex){
5018         this.config[col].dataIndex = dataIndex;
5019     },
5020
5021     
5022     
5023     /**
5024      * Returns true if the cell is editable.
5025      * @param {Number} colIndex The column index
5026      * @param {Number} rowIndex The row index
5027      * @return {Boolean}
5028      */
5029     isCellEditable : function(colIndex, rowIndex){
5030         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5031     },
5032
5033     /**
5034      * Returns the editor defined for the cell/column.
5035      * return false or null to disable editing.
5036      * @param {Number} colIndex The column index
5037      * @param {Number} rowIndex The row index
5038      * @return {Object}
5039      */
5040     getCellEditor : function(colIndex, rowIndex){
5041         return this.config[colIndex].editor;
5042     },
5043
5044     /**
5045      * Sets if a column is editable.
5046      * @param {Number} col The column index
5047      * @param {Boolean} editable True if the column is editable
5048      */
5049     setEditable : function(col, editable){
5050         this.config[col].editable = editable;
5051     },
5052
5053
5054     /**
5055      * Returns true if the column is hidden.
5056      * @param {Number} colIndex The column index
5057      * @return {Boolean}
5058      */
5059     isHidden : function(colIndex){
5060         return this.config[colIndex].hidden;
5061     },
5062
5063
5064     /**
5065      * Returns true if the column width cannot be changed
5066      */
5067     isFixed : function(colIndex){
5068         return this.config[colIndex].fixed;
5069     },
5070
5071     /**
5072      * Returns true if the column can be resized
5073      * @return {Boolean}
5074      */
5075     isResizable : function(colIndex){
5076         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5077     },
5078     /**
5079      * Sets if a column is hidden.
5080      * @param {Number} colIndex The column index
5081      * @param {Boolean} hidden True if the column is hidden
5082      */
5083     setHidden : function(colIndex, hidden){
5084         this.config[colIndex].hidden = hidden;
5085         this.totalWidth = null;
5086         this.fireEvent("hiddenchange", this, colIndex, hidden);
5087     },
5088
5089     /**
5090      * Sets the editor for a column.
5091      * @param {Number} col The column index
5092      * @param {Object} editor The editor object
5093      */
5094     setEditor : function(col, editor){
5095         this.config[col].editor = editor;
5096     }
5097 });
5098
5099 Roo.grid.ColumnModel.defaultRenderer = function(value){
5100         if(typeof value == "string" && value.length < 1){
5101             return "&#160;";
5102         }
5103         return value;
5104 };
5105
5106 // Alias for backwards compatibility
5107 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5108 /*
5109  * Based on:
5110  * Ext JS Library 1.1.1
5111  * Copyright(c) 2006-2007, Ext JS, LLC.
5112  *
5113  * Originally Released Under LGPL - original licence link has changed is not relivant.
5114  *
5115  * Fork - LGPL
5116  * <script type="text/javascript">
5117  */
5118  
5119 /**
5120  * @class Roo.LoadMask
5121  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5122  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5123  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5124  * element's UpdateManager load indicator and will be destroyed after the initial load.
5125  * @constructor
5126  * Create a new LoadMask
5127  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5128  * @param {Object} config The config object
5129  */
5130 Roo.LoadMask = function(el, config){
5131     this.el = Roo.get(el);
5132     Roo.apply(this, config);
5133     if(this.store){
5134         this.store.on('beforeload', this.onBeforeLoad, this);
5135         this.store.on('load', this.onLoad, this);
5136         this.store.on('loadexception', this.onLoadException, this);
5137         this.removeMask = false;
5138     }else{
5139         var um = this.el.getUpdateManager();
5140         um.showLoadIndicator = false; // disable the default indicator
5141         um.on('beforeupdate', this.onBeforeLoad, this);
5142         um.on('update', this.onLoad, this);
5143         um.on('failure', this.onLoad, this);
5144         this.removeMask = true;
5145     }
5146 };
5147
5148 Roo.LoadMask.prototype = {
5149     /**
5150      * @cfg {Boolean} removeMask
5151      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5152      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5153      */
5154     /**
5155      * @cfg {String} msg
5156      * The text to display in a centered loading message box (defaults to 'Loading...')
5157      */
5158     msg : 'Loading...',
5159     /**
5160      * @cfg {String} msgCls
5161      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5162      */
5163     msgCls : 'x-mask-loading',
5164
5165     /**
5166      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5167      * @type Boolean
5168      */
5169     disabled: false,
5170
5171     /**
5172      * Disables the mask to prevent it from being displayed
5173      */
5174     disable : function(){
5175        this.disabled = true;
5176     },
5177
5178     /**
5179      * Enables the mask so that it can be displayed
5180      */
5181     enable : function(){
5182         this.disabled = false;
5183     },
5184     
5185     onLoadException : function()
5186     {
5187         Roo.log(arguments);
5188         
5189         if (typeof(arguments[3]) != 'undefined') {
5190             Roo.MessageBox.alert("Error loading",arguments[3]);
5191         } 
5192         /*
5193         try {
5194             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5195                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5196             }   
5197         } catch(e) {
5198             
5199         }
5200         */
5201     
5202         
5203         
5204         this.el.unmask(this.removeMask);
5205     },
5206     // private
5207     onLoad : function()
5208     {
5209         this.el.unmask(this.removeMask);
5210     },
5211
5212     // private
5213     onBeforeLoad : function(){
5214         if(!this.disabled){
5215             this.el.mask(this.msg, this.msgCls);
5216         }
5217     },
5218
5219     // private
5220     destroy : function(){
5221         if(this.store){
5222             this.store.un('beforeload', this.onBeforeLoad, this);
5223             this.store.un('load', this.onLoad, this);
5224             this.store.un('loadexception', this.onLoadException, this);
5225         }else{
5226             var um = this.el.getUpdateManager();
5227             um.un('beforeupdate', this.onBeforeLoad, this);
5228             um.un('update', this.onLoad, this);
5229             um.un('failure', this.onLoad, this);
5230         }
5231     }
5232 };/*
5233  * - LGPL
5234  *
5235  * table
5236  * 
5237  */
5238
5239 /**
5240  * @class Roo.bootstrap.Table
5241  * @extends Roo.bootstrap.Component
5242  * Bootstrap Table class
5243  * @cfg {String} cls table class
5244  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5245  * @cfg {String} bgcolor Specifies the background color for a table
5246  * @cfg {Number} border Specifies whether the table cells should have borders or not
5247  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5248  * @cfg {Number} cellspacing Specifies the space between cells
5249  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5250  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5251  * @cfg {String} sortable Specifies that the table should be sortable
5252  * @cfg {String} summary Specifies a summary of the content of a table
5253  * @cfg {Number} width Specifies the width of a table
5254  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5255  * 
5256  * @cfg {boolean} striped Should the rows be alternative striped
5257  * @cfg {boolean} bordered Add borders to the table
5258  * @cfg {boolean} hover Add hover highlighting
5259  * @cfg {boolean} condensed Format condensed
5260  * @cfg {boolean} responsive Format condensed
5261  * @cfg {Boolean} loadMask (true|false) default false
5262  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5263  * @cfg {Boolean} thead (true|false) generate thead, default true
5264  * @cfg {Boolean} RowSelection (true|false) default false
5265  * @cfg {Boolean} CellSelection (true|false) default false
5266  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5267  
5268  * 
5269  * @constructor
5270  * Create a new Table
5271  * @param {Object} config The config object
5272  */
5273
5274 Roo.bootstrap.Table = function(config){
5275     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5276     
5277     if (this.sm) {
5278         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5279         this.sm = this.selModel;
5280         this.sm.xmodule = this.xmodule || false;
5281     }
5282     if (this.cm && typeof(this.cm.config) == 'undefined') {
5283         this.colModel = new Roo.grid.ColumnModel(this.cm);
5284         this.cm = this.colModel;
5285         this.cm.xmodule = this.xmodule || false;
5286     }
5287     if (this.store) {
5288         this.store= Roo.factory(this.store, Roo.data);
5289         this.ds = this.store;
5290         this.ds.xmodule = this.xmodule || false;
5291          
5292     }
5293     if (this.footer && this.store) {
5294         this.footer.dataSource = this.ds;
5295         this.footer = Roo.factory(this.footer);
5296     }
5297     
5298     /** @private */
5299     this.addEvents({
5300         /**
5301          * @event cellclick
5302          * Fires when a cell is clicked
5303          * @param {Roo.bootstrap.Table} this
5304          * @param {Roo.Element} el
5305          * @param {Number} rowIndex
5306          * @param {Number} columnIndex
5307          * @param {Roo.EventObject} e
5308          */
5309         "cellclick" : true,
5310         /**
5311          * @event celldblclick
5312          * Fires when a cell is double clicked
5313          * @param {Roo.bootstrap.Table} this
5314          * @param {Roo.Element} el
5315          * @param {Number} rowIndex
5316          * @param {Number} columnIndex
5317          * @param {Roo.EventObject} e
5318          */
5319         "celldblclick" : true,
5320         /**
5321          * @event rowclick
5322          * Fires when a row is clicked
5323          * @param {Roo.bootstrap.Table} this
5324          * @param {Roo.Element} el
5325          * @param {Number} rowIndex
5326          * @param {Roo.EventObject} e
5327          */
5328         "rowclick" : true,
5329         /**
5330          * @event rowdblclick
5331          * Fires when a row is double clicked
5332          * @param {Roo.bootstrap.Table} this
5333          * @param {Roo.Element} el
5334          * @param {Number} rowIndex
5335          * @param {Roo.EventObject} e
5336          */
5337         "rowdblclick" : true,
5338         /**
5339          * @event mouseover
5340          * Fires when a mouseover occur
5341          * @param {Roo.bootstrap.Table} this
5342          * @param {Roo.Element} el
5343          * @param {Number} rowIndex
5344          * @param {Number} columnIndex
5345          * @param {Roo.EventObject} e
5346          */
5347         "mouseover" : true,
5348         /**
5349          * @event mouseout
5350          * Fires when a mouseout occur
5351          * @param {Roo.bootstrap.Table} this
5352          * @param {Roo.Element} el
5353          * @param {Number} rowIndex
5354          * @param {Number} columnIndex
5355          * @param {Roo.EventObject} e
5356          */
5357         "mouseout" : true,
5358         /**
5359          * @event rowclass
5360          * Fires when a row is rendered, so you can change add a style to it.
5361          * @param {Roo.bootstrap.Table} this
5362          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5363          */
5364         'rowclass' : true,
5365           /**
5366          * @event rowsrendered
5367          * Fires when all the  rows have been rendered
5368          * @param {Roo.bootstrap.Table} this
5369          */
5370         'rowsrendered' : true
5371         
5372     });
5373 };
5374
5375 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5376     
5377     cls: false,
5378     align: false,
5379     bgcolor: false,
5380     border: false,
5381     cellpadding: false,
5382     cellspacing: false,
5383     frame: false,
5384     rules: false,
5385     sortable: false,
5386     summary: false,
5387     width: false,
5388     striped : false,
5389     bordered: false,
5390     hover:  false,
5391     condensed : false,
5392     responsive : false,
5393     sm : false,
5394     cm : false,
5395     store : false,
5396     loadMask : false,
5397     tfoot : true,
5398     thead : true,
5399     RowSelection : false,
5400     CellSelection : false,
5401     layout : false,
5402     
5403     // Roo.Element - the tbody
5404     mainBody: false, 
5405     
5406     getAutoCreate : function(){
5407         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5408         
5409         cfg = {
5410             tag: 'table',
5411             cls : 'table',
5412             cn : []
5413         }
5414             
5415         if (this.striped) {
5416             cfg.cls += ' table-striped';
5417         }
5418         
5419         if (this.hover) {
5420             cfg.cls += ' table-hover';
5421         }
5422         if (this.bordered) {
5423             cfg.cls += ' table-bordered';
5424         }
5425         if (this.condensed) {
5426             cfg.cls += ' table-condensed';
5427         }
5428         if (this.responsive) {
5429             cfg.cls += ' table-responsive';
5430         }
5431         
5432         if (this.cls) {
5433             cfg.cls+=  ' ' +this.cls;
5434         }
5435         
5436         // this lot should be simplifed...
5437         
5438         if (this.align) {
5439             cfg.align=this.align;
5440         }
5441         if (this.bgcolor) {
5442             cfg.bgcolor=this.bgcolor;
5443         }
5444         if (this.border) {
5445             cfg.border=this.border;
5446         }
5447         if (this.cellpadding) {
5448             cfg.cellpadding=this.cellpadding;
5449         }
5450         if (this.cellspacing) {
5451             cfg.cellspacing=this.cellspacing;
5452         }
5453         if (this.frame) {
5454             cfg.frame=this.frame;
5455         }
5456         if (this.rules) {
5457             cfg.rules=this.rules;
5458         }
5459         if (this.sortable) {
5460             cfg.sortable=this.sortable;
5461         }
5462         if (this.summary) {
5463             cfg.summary=this.summary;
5464         }
5465         if (this.width) {
5466             cfg.width=this.width;
5467         }
5468         if (this.layout) {
5469             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5470         }
5471         
5472         if(this.store || this.cm){
5473             if(this.thead){
5474                 cfg.cn.push(this.renderHeader());
5475             }
5476             
5477             cfg.cn.push(this.renderBody());
5478             
5479             if(this.tfoot){
5480                 cfg.cn.push(this.renderFooter());
5481             }
5482             
5483             cfg.cls+=  ' TableGrid';
5484         }
5485         
5486         return { cn : [ cfg ] };
5487     },
5488     
5489     initEvents : function()
5490     {   
5491         if(!this.store || !this.cm){
5492             return;
5493         }
5494         
5495         //Roo.log('initEvents with ds!!!!');
5496         
5497         this.mainBody = this.el.select('tbody', true).first();
5498         
5499         
5500         var _this = this;
5501         
5502         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5503             e.on('click', _this.sort, _this);
5504         });
5505         
5506         this.el.on("click", this.onClick, this);
5507         this.el.on("dblclick", this.onDblClick, this);
5508         
5509         // why is this done????? = it breaks dialogs??
5510         //this.parent().el.setStyle('position', 'relative');
5511         
5512         
5513         if (this.footer) {
5514             this.footer.parentId = this.id;
5515             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5516         }
5517         
5518         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5519         
5520         this.store.on('load', this.onLoad, this);
5521         this.store.on('beforeload', this.onBeforeLoad, this);
5522         this.store.on('update', this.onUpdate, this);
5523         this.store.on('add', this.onAdd, this);
5524         
5525     },
5526     
5527     onMouseover : function(e, el)
5528     {
5529         var cell = Roo.get(el);
5530         
5531         if(!cell){
5532             return;
5533         }
5534         
5535         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5536             cell = cell.findParent('td', false, true);
5537         }
5538         
5539         var row = cell.findParent('tr', false, true);
5540         var cellIndex = cell.dom.cellIndex;
5541         var rowIndex = row.dom.rowIndex - 1; // start from 0
5542         
5543         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5544         
5545     },
5546     
5547     onMouseout : function(e, el)
5548     {
5549         var cell = Roo.get(el);
5550         
5551         if(!cell){
5552             return;
5553         }
5554         
5555         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5556             cell = cell.findParent('td', false, true);
5557         }
5558         
5559         var row = cell.findParent('tr', false, true);
5560         var cellIndex = cell.dom.cellIndex;
5561         var rowIndex = row.dom.rowIndex - 1; // start from 0
5562         
5563         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5564         
5565     },
5566     
5567     onClick : function(e, el)
5568     {
5569         var cell = Roo.get(el);
5570         
5571         if(!cell || (!this.CellSelection && !this.RowSelection)){
5572             return;
5573         }
5574         
5575         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5576             cell = cell.findParent('td', false, true);
5577         }
5578         
5579         if(!cell || typeof(cell) == 'undefined'){
5580             return;
5581         }
5582         
5583         var row = cell.findParent('tr', false, true);
5584         
5585         if(!row || typeof(row) == 'undefined'){
5586             return;
5587         }
5588         
5589         var cellIndex = cell.dom.cellIndex;
5590         var rowIndex = this.getRowIndex(row);
5591         
5592         if(this.CellSelection){
5593             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5594         }
5595         
5596         if(this.RowSelection){
5597             this.fireEvent('rowclick', this, row, rowIndex, e);
5598         }
5599         
5600         
5601     },
5602     
5603     onDblClick : function(e,el)
5604     {
5605         var cell = Roo.get(el);
5606         
5607         if(!cell || (!this.CellSelection && !this.RowSelection)){
5608             return;
5609         }
5610         
5611         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5612             cell = cell.findParent('td', false, true);
5613         }
5614         
5615         if(!cell || typeof(cell) == 'undefined'){
5616             return;
5617         }
5618         
5619         var row = cell.findParent('tr', false, true);
5620         
5621         if(!row || typeof(row) == 'undefined'){
5622             return;
5623         }
5624         
5625         var cellIndex = cell.dom.cellIndex;
5626         var rowIndex = this.getRowIndex(row);
5627         
5628         if(this.CellSelection){
5629             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5630         }
5631         
5632         if(this.RowSelection){
5633             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5634         }
5635     },
5636     
5637     sort : function(e,el)
5638     {
5639         var col = Roo.get(el);
5640         
5641         if(!col.hasClass('sortable')){
5642             return;
5643         }
5644         
5645         var sort = col.attr('sort');
5646         var dir = 'ASC';
5647         
5648         if(col.hasClass('glyphicon-arrow-up')){
5649             dir = 'DESC';
5650         }
5651         
5652         this.store.sortInfo = {field : sort, direction : dir};
5653         
5654         if (this.footer) {
5655             Roo.log("calling footer first");
5656             this.footer.onClick('first');
5657         } else {
5658         
5659             this.store.load({ params : { start : 0 } });
5660         }
5661     },
5662     
5663     renderHeader : function()
5664     {
5665         var header = {
5666             tag: 'thead',
5667             cn : []
5668         };
5669         
5670         var cm = this.cm;
5671         
5672         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5673             
5674             var config = cm.config[i];
5675                     
5676             var c = {
5677                 tag: 'th',
5678                 style : '',
5679                 html: cm.getColumnHeader(i)
5680             };
5681             
5682             if(typeof(config.tooltip) != 'undefined'){
5683                 c.tooltip = config.tooltip;
5684             }
5685             
5686             if(typeof(config.colspan) != 'undefined'){
5687                 c.colspan = config.colspan;
5688             }
5689             
5690             if(typeof(config.hidden) != 'undefined' && config.hidden){
5691                 c.style += ' display:none;';
5692             }
5693             
5694             if(typeof(config.dataIndex) != 'undefined'){
5695                 c.sort = config.dataIndex;
5696             }
5697             
5698             if(typeof(config.sortable) != 'undefined' && config.sortable){
5699                 c.cls = 'sortable';
5700             }
5701             
5702             if(typeof(config.align) != 'undefined' && config.align.length){
5703                 c.style += ' text-align:' + config.align + ';';
5704             }
5705             
5706             if(typeof(config.width) != 'undefined'){
5707                 c.style += ' width:' + config.width + 'px;';
5708             }
5709             
5710             if(typeof(config.cls) != 'undefined'){
5711                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5712             }
5713             
5714             header.cn.push(c)
5715         }
5716         
5717         return header;
5718     },
5719     
5720     renderBody : function()
5721     {
5722         var body = {
5723             tag: 'tbody',
5724             cn : [
5725                 {
5726                     tag: 'tr',
5727                     cn : [
5728                         {
5729                             tag : 'td',
5730                             colspan :  this.cm.getColumnCount()
5731                         }
5732                     ]
5733                 }
5734             ]
5735         };
5736         
5737         return body;
5738     },
5739     
5740     renderFooter : function()
5741     {
5742         var footer = {
5743             tag: 'tfoot',
5744             cn : [
5745                 {
5746                     tag: 'tr',
5747                     cn : [
5748                         {
5749                             tag : 'td',
5750                             colspan :  this.cm.getColumnCount()
5751                         }
5752                     ]
5753                 }
5754             ]
5755         };
5756         
5757         return footer;
5758     },
5759     
5760     
5761     
5762     onLoad : function()
5763     {
5764         Roo.log('ds onload');
5765         this.clear();
5766         
5767         var _this = this;
5768         var cm = this.cm;
5769         var ds = this.store;
5770         
5771         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5772             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5773             
5774             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5775                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5776             }
5777             
5778             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5779                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5780             }
5781         });
5782         
5783         var tbody =  this.mainBody;
5784               
5785         if(ds.getCount() > 0){
5786             ds.data.each(function(d,rowIndex){
5787                 var row =  this.renderRow(cm, ds, rowIndex);
5788                 
5789                 tbody.createChild(row);
5790                 
5791                 var _this = this;
5792                 
5793                 if(row.cellObjects.length){
5794                     Roo.each(row.cellObjects, function(r){
5795                         _this.renderCellObject(r);
5796                     })
5797                 }
5798                 
5799             }, this);
5800         }
5801         
5802         Roo.each(this.el.select('tbody td', true).elements, function(e){
5803             e.on('mouseover', _this.onMouseover, _this);
5804         });
5805         
5806         Roo.each(this.el.select('tbody td', true).elements, function(e){
5807             e.on('mouseout', _this.onMouseout, _this);
5808         });
5809         this.fireEvent('rowsrendered', this);
5810         //if(this.loadMask){
5811         //    this.maskEl.hide();
5812         //}
5813     },
5814     
5815     
5816     onUpdate : function(ds,record)
5817     {
5818         this.refreshRow(record);
5819     },
5820     
5821     onRemove : function(ds, record, index, isUpdate){
5822         if(isUpdate !== true){
5823             this.fireEvent("beforerowremoved", this, index, record);
5824         }
5825         var bt = this.mainBody.dom;
5826         
5827         var rows = this.el.select('tbody > tr', true).elements;
5828         
5829         if(typeof(rows[index]) != 'undefined'){
5830             bt.removeChild(rows[index].dom);
5831         }
5832         
5833 //        if(bt.rows[index]){
5834 //            bt.removeChild(bt.rows[index]);
5835 //        }
5836         
5837         if(isUpdate !== true){
5838             //this.stripeRows(index);
5839             //this.syncRowHeights(index, index);
5840             //this.layout();
5841             this.fireEvent("rowremoved", this, index, record);
5842         }
5843     },
5844     
5845     onAdd : function(ds, records, rowIndex)
5846     {
5847         //Roo.log('on Add called');
5848         // - note this does not handle multiple adding very well..
5849         var bt = this.mainBody.dom;
5850         for (var i =0 ; i < records.length;i++) {
5851             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5852             //Roo.log(records[i]);
5853             //Roo.log(this.store.getAt(rowIndex+i));
5854             this.insertRow(this.store, rowIndex + i, false);
5855             return;
5856         }
5857         
5858     },
5859     
5860     
5861     refreshRow : function(record){
5862         var ds = this.store, index;
5863         if(typeof record == 'number'){
5864             index = record;
5865             record = ds.getAt(index);
5866         }else{
5867             index = ds.indexOf(record);
5868         }
5869         this.insertRow(ds, index, true);
5870         this.onRemove(ds, record, index+1, true);
5871         //this.syncRowHeights(index, index);
5872         //this.layout();
5873         this.fireEvent("rowupdated", this, index, record);
5874     },
5875     
5876     insertRow : function(dm, rowIndex, isUpdate){
5877         
5878         if(!isUpdate){
5879             this.fireEvent("beforerowsinserted", this, rowIndex);
5880         }
5881             //var s = this.getScrollState();
5882         var row = this.renderRow(this.cm, this.store, rowIndex);
5883         // insert before rowIndex..
5884         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5885         
5886         var _this = this;
5887                 
5888         if(row.cellObjects.length){
5889             Roo.each(row.cellObjects, function(r){
5890                 _this.renderCellObject(r);
5891             })
5892         }
5893             
5894         if(!isUpdate){
5895             this.fireEvent("rowsinserted", this, rowIndex);
5896             //this.syncRowHeights(firstRow, lastRow);
5897             //this.stripeRows(firstRow);
5898             //this.layout();
5899         }
5900         
5901     },
5902     
5903     
5904     getRowDom : function(rowIndex)
5905     {
5906         var rows = this.el.select('tbody > tr', true).elements;
5907         
5908         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5909         
5910     },
5911     // returns the object tree for a tr..
5912   
5913     
5914     renderRow : function(cm, ds, rowIndex) 
5915     {
5916         
5917         var d = ds.getAt(rowIndex);
5918         
5919         var row = {
5920             tag : 'tr',
5921             cn : []
5922         };
5923             
5924         var cellObjects = [];
5925         
5926         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5927             var config = cm.config[i];
5928             
5929             var renderer = cm.getRenderer(i);
5930             var value = '';
5931             var id = false;
5932             
5933             if(typeof(renderer) !== 'undefined'){
5934                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5935             }
5936             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5937             // and are rendered into the cells after the row is rendered - using the id for the element.
5938             
5939             if(typeof(value) === 'object'){
5940                 id = Roo.id();
5941                 cellObjects.push({
5942                     container : id,
5943                     cfg : value 
5944                 })
5945             }
5946             
5947             var rowcfg = {
5948                 record: d,
5949                 rowIndex : rowIndex,
5950                 colIndex : i,
5951                 rowClass : ''
5952             }
5953
5954             this.fireEvent('rowclass', this, rowcfg);
5955             
5956             var td = {
5957                 tag: 'td',
5958                 cls : rowcfg.rowClass,
5959                 style: '',
5960                 html: (typeof(value) === 'object') ? '' : value
5961             };
5962             
5963             if (id) {
5964                 td.id = id;
5965             }
5966             
5967             if(typeof(config.colspan) != 'undefined'){
5968                 td.colspan = config.colspan;
5969             }
5970             
5971             if(typeof(config.hidden) != 'undefined' && config.hidden){
5972                 td.style += ' display:none;';
5973             }
5974             
5975             if(typeof(config.align) != 'undefined' && config.align.length){
5976                 td.style += ' text-align:' + config.align + ';';
5977             }
5978             
5979             if(typeof(config.width) != 'undefined'){
5980                 td.style += ' width:' +  config.width + 'px;';
5981             }
5982             
5983             if(typeof(config.cursor) != 'undefined'){
5984                 td.style += ' cursor:' +  config.cursor + ';';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5989             }
5990              
5991             row.cn.push(td);
5992            
5993         }
5994         
5995         row.cellObjects = cellObjects;
5996         
5997         return row;
5998           
5999     },
6000     
6001     
6002     
6003     onBeforeLoad : function()
6004     {
6005         //Roo.log('ds onBeforeLoad');
6006         
6007         //this.clear();
6008         
6009         //if(this.loadMask){
6010         //    this.maskEl.show();
6011         //}
6012     },
6013      /**
6014      * Remove all rows
6015      */
6016     clear : function()
6017     {
6018         this.el.select('tbody', true).first().dom.innerHTML = '';
6019     },
6020     /**
6021      * Show or hide a row.
6022      * @param {Number} rowIndex to show or hide
6023      * @param {Boolean} state hide
6024      */
6025     setRowVisibility : function(rowIndex, state)
6026     {
6027         var bt = this.mainBody.dom;
6028         
6029         var rows = this.el.select('tbody > tr', true).elements;
6030         
6031         if(typeof(rows[rowIndex]) == 'undefined'){
6032             return;
6033         }
6034         rows[rowIndex].dom.style.display = state ? '' : 'none';
6035     },
6036     
6037     
6038     getSelectionModel : function(){
6039         if(!this.selModel){
6040             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6041         }
6042         return this.selModel;
6043     },
6044     /*
6045      * Render the Roo.bootstrap object from renderder
6046      */
6047     renderCellObject : function(r)
6048     {
6049         var _this = this;
6050         
6051         var t = r.cfg.render(r.container);
6052         
6053         if(r.cfg.cn){
6054             Roo.each(r.cfg.cn, function(c){
6055                 var child = {
6056                     container: t.getChildContainer(),
6057                     cfg: c
6058                 }
6059                 _this.renderCellObject(child);
6060             })
6061         }
6062     },
6063     
6064     getRowIndex : function(row)
6065     {
6066         var rowIndex = -1;
6067         
6068         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6069             if(el != row){
6070                 return;
6071             }
6072             
6073             rowIndex = index;
6074         });
6075         
6076         return rowIndex;
6077     }
6078    
6079 });
6080
6081  
6082
6083  /*
6084  * - LGPL
6085  *
6086  * table cell
6087  * 
6088  */
6089
6090 /**
6091  * @class Roo.bootstrap.TableCell
6092  * @extends Roo.bootstrap.Component
6093  * Bootstrap TableCell class
6094  * @cfg {String} html cell contain text
6095  * @cfg {String} cls cell class
6096  * @cfg {String} tag cell tag (td|th) default td
6097  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6098  * @cfg {String} align Aligns the content in a cell
6099  * @cfg {String} axis Categorizes cells
6100  * @cfg {String} bgcolor Specifies the background color of a cell
6101  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6102  * @cfg {Number} colspan Specifies the number of columns a cell should span
6103  * @cfg {String} headers Specifies one or more header cells a cell is related to
6104  * @cfg {Number} height Sets the height of a cell
6105  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6106  * @cfg {Number} rowspan Sets the number of rows a cell should span
6107  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6108  * @cfg {String} valign Vertical aligns the content in a cell
6109  * @cfg {Number} width Specifies the width of a cell
6110  * 
6111  * @constructor
6112  * Create a new TableCell
6113  * @param {Object} config The config object
6114  */
6115
6116 Roo.bootstrap.TableCell = function(config){
6117     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6118 };
6119
6120 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6121     
6122     html: false,
6123     cls: false,
6124     tag: false,
6125     abbr: false,
6126     align: false,
6127     axis: false,
6128     bgcolor: false,
6129     charoff: false,
6130     colspan: false,
6131     headers: false,
6132     height: false,
6133     nowrap: false,
6134     rowspan: false,
6135     scope: false,
6136     valign: false,
6137     width: false,
6138     
6139     
6140     getAutoCreate : function(){
6141         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6142         
6143         cfg = {
6144             tag: 'td'
6145         }
6146         
6147         if(this.tag){
6148             cfg.tag = this.tag;
6149         }
6150         
6151         if (this.html) {
6152             cfg.html=this.html
6153         }
6154         if (this.cls) {
6155             cfg.cls=this.cls
6156         }
6157         if (this.abbr) {
6158             cfg.abbr=this.abbr
6159         }
6160         if (this.align) {
6161             cfg.align=this.align
6162         }
6163         if (this.axis) {
6164             cfg.axis=this.axis
6165         }
6166         if (this.bgcolor) {
6167             cfg.bgcolor=this.bgcolor
6168         }
6169         if (this.charoff) {
6170             cfg.charoff=this.charoff
6171         }
6172         if (this.colspan) {
6173             cfg.colspan=this.colspan
6174         }
6175         if (this.headers) {
6176             cfg.headers=this.headers
6177         }
6178         if (this.height) {
6179             cfg.height=this.height
6180         }
6181         if (this.nowrap) {
6182             cfg.nowrap=this.nowrap
6183         }
6184         if (this.rowspan) {
6185             cfg.rowspan=this.rowspan
6186         }
6187         if (this.scope) {
6188             cfg.scope=this.scope
6189         }
6190         if (this.valign) {
6191             cfg.valign=this.valign
6192         }
6193         if (this.width) {
6194             cfg.width=this.width
6195         }
6196         
6197         
6198         return cfg;
6199     }
6200    
6201 });
6202
6203  
6204
6205  /*
6206  * - LGPL
6207  *
6208  * table row
6209  * 
6210  */
6211
6212 /**
6213  * @class Roo.bootstrap.TableRow
6214  * @extends Roo.bootstrap.Component
6215  * Bootstrap TableRow class
6216  * @cfg {String} cls row class
6217  * @cfg {String} align Aligns the content in a table row
6218  * @cfg {String} bgcolor Specifies a background color for a table row
6219  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6220  * @cfg {String} valign Vertical aligns the content in a table row
6221  * 
6222  * @constructor
6223  * Create a new TableRow
6224  * @param {Object} config The config object
6225  */
6226
6227 Roo.bootstrap.TableRow = function(config){
6228     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6229 };
6230
6231 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6232     
6233     cls: false,
6234     align: false,
6235     bgcolor: false,
6236     charoff: false,
6237     valign: false,
6238     
6239     getAutoCreate : function(){
6240         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6241         
6242         cfg = {
6243             tag: 'tr'
6244         }
6245             
6246         if(this.cls){
6247             cfg.cls = this.cls;
6248         }
6249         if(this.align){
6250             cfg.align = this.align;
6251         }
6252         if(this.bgcolor){
6253             cfg.bgcolor = this.bgcolor;
6254         }
6255         if(this.charoff){
6256             cfg.charoff = this.charoff;
6257         }
6258         if(this.valign){
6259             cfg.valign = this.valign;
6260         }
6261         
6262         return cfg;
6263     }
6264    
6265 });
6266
6267  
6268
6269  /*
6270  * - LGPL
6271  *
6272  * table body
6273  * 
6274  */
6275
6276 /**
6277  * @class Roo.bootstrap.TableBody
6278  * @extends Roo.bootstrap.Component
6279  * Bootstrap TableBody class
6280  * @cfg {String} cls element class
6281  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6282  * @cfg {String} align Aligns the content inside the element
6283  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6284  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6285  * 
6286  * @constructor
6287  * Create a new TableBody
6288  * @param {Object} config The config object
6289  */
6290
6291 Roo.bootstrap.TableBody = function(config){
6292     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6293 };
6294
6295 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6296     
6297     cls: false,
6298     tag: false,
6299     align: false,
6300     charoff: false,
6301     valign: false,
6302     
6303     getAutoCreate : function(){
6304         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6305         
6306         cfg = {
6307             tag: 'tbody'
6308         }
6309             
6310         if (this.cls) {
6311             cfg.cls=this.cls
6312         }
6313         if(this.tag){
6314             cfg.tag = this.tag;
6315         }
6316         
6317         if(this.align){
6318             cfg.align = this.align;
6319         }
6320         if(this.charoff){
6321             cfg.charoff = this.charoff;
6322         }
6323         if(this.valign){
6324             cfg.valign = this.valign;
6325         }
6326         
6327         return cfg;
6328     }
6329     
6330     
6331 //    initEvents : function()
6332 //    {
6333 //        
6334 //        if(!this.store){
6335 //            return;
6336 //        }
6337 //        
6338 //        this.store = Roo.factory(this.store, Roo.data);
6339 //        this.store.on('load', this.onLoad, this);
6340 //        
6341 //        this.store.load();
6342 //        
6343 //    },
6344 //    
6345 //    onLoad: function () 
6346 //    {   
6347 //        this.fireEvent('load', this);
6348 //    }
6349 //    
6350 //   
6351 });
6352
6353  
6354
6355  /*
6356  * Based on:
6357  * Ext JS Library 1.1.1
6358  * Copyright(c) 2006-2007, Ext JS, LLC.
6359  *
6360  * Originally Released Under LGPL - original licence link has changed is not relivant.
6361  *
6362  * Fork - LGPL
6363  * <script type="text/javascript">
6364  */
6365
6366 // as we use this in bootstrap.
6367 Roo.namespace('Roo.form');
6368  /**
6369  * @class Roo.form.Action
6370  * Internal Class used to handle form actions
6371  * @constructor
6372  * @param {Roo.form.BasicForm} el The form element or its id
6373  * @param {Object} config Configuration options
6374  */
6375
6376  
6377  
6378 // define the action interface
6379 Roo.form.Action = function(form, options){
6380     this.form = form;
6381     this.options = options || {};
6382 };
6383 /**
6384  * Client Validation Failed
6385  * @const 
6386  */
6387 Roo.form.Action.CLIENT_INVALID = 'client';
6388 /**
6389  * Server Validation Failed
6390  * @const 
6391  */
6392 Roo.form.Action.SERVER_INVALID = 'server';
6393  /**
6394  * Connect to Server Failed
6395  * @const 
6396  */
6397 Roo.form.Action.CONNECT_FAILURE = 'connect';
6398 /**
6399  * Reading Data from Server Failed
6400  * @const 
6401  */
6402 Roo.form.Action.LOAD_FAILURE = 'load';
6403
6404 Roo.form.Action.prototype = {
6405     type : 'default',
6406     failureType : undefined,
6407     response : undefined,
6408     result : undefined,
6409
6410     // interface method
6411     run : function(options){
6412
6413     },
6414
6415     // interface method
6416     success : function(response){
6417
6418     },
6419
6420     // interface method
6421     handleResponse : function(response){
6422
6423     },
6424
6425     // default connection failure
6426     failure : function(response){
6427         
6428         this.response = response;
6429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6430         this.form.afterAction(this, false);
6431     },
6432
6433     processResponse : function(response){
6434         this.response = response;
6435         if(!response.responseText){
6436             return true;
6437         }
6438         this.result = this.handleResponse(response);
6439         return this.result;
6440     },
6441
6442     // utility functions used internally
6443     getUrl : function(appendParams){
6444         var url = this.options.url || this.form.url || this.form.el.dom.action;
6445         if(appendParams){
6446             var p = this.getParams();
6447             if(p){
6448                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6449             }
6450         }
6451         return url;
6452     },
6453
6454     getMethod : function(){
6455         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6456     },
6457
6458     getParams : function(){
6459         var bp = this.form.baseParams;
6460         var p = this.options.params;
6461         if(p){
6462             if(typeof p == "object"){
6463                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6464             }else if(typeof p == 'string' && bp){
6465                 p += '&' + Roo.urlEncode(bp);
6466             }
6467         }else if(bp){
6468             p = Roo.urlEncode(bp);
6469         }
6470         return p;
6471     },
6472
6473     createCallback : function(){
6474         return {
6475             success: this.success,
6476             failure: this.failure,
6477             scope: this,
6478             timeout: (this.form.timeout*1000),
6479             upload: this.form.fileUpload ? this.success : undefined
6480         };
6481     }
6482 };
6483
6484 Roo.form.Action.Submit = function(form, options){
6485     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6486 };
6487
6488 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6489     type : 'submit',
6490
6491     haveProgress : false,
6492     uploadComplete : false,
6493     
6494     // uploadProgress indicator.
6495     uploadProgress : function()
6496     {
6497         if (!this.form.progressUrl) {
6498             return;
6499         }
6500         
6501         if (!this.haveProgress) {
6502             Roo.MessageBox.progress("Uploading", "Uploading");
6503         }
6504         if (this.uploadComplete) {
6505            Roo.MessageBox.hide();
6506            return;
6507         }
6508         
6509         this.haveProgress = true;
6510    
6511         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6512         
6513         var c = new Roo.data.Connection();
6514         c.request({
6515             url : this.form.progressUrl,
6516             params: {
6517                 id : uid
6518             },
6519             method: 'GET',
6520             success : function(req){
6521                //console.log(data);
6522                 var rdata = false;
6523                 var edata;
6524                 try  {
6525                    rdata = Roo.decode(req.responseText)
6526                 } catch (e) {
6527                     Roo.log("Invalid data from server..");
6528                     Roo.log(edata);
6529                     return;
6530                 }
6531                 if (!rdata || !rdata.success) {
6532                     Roo.log(rdata);
6533                     Roo.MessageBox.alert(Roo.encode(rdata));
6534                     return;
6535                 }
6536                 var data = rdata.data;
6537                 
6538                 if (this.uploadComplete) {
6539                    Roo.MessageBox.hide();
6540                    return;
6541                 }
6542                    
6543                 if (data){
6544                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6545                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6546                     );
6547                 }
6548                 this.uploadProgress.defer(2000,this);
6549             },
6550        
6551             failure: function(data) {
6552                 Roo.log('progress url failed ');
6553                 Roo.log(data);
6554             },
6555             scope : this
6556         });
6557            
6558     },
6559     
6560     
6561     run : function()
6562     {
6563         // run get Values on the form, so it syncs any secondary forms.
6564         this.form.getValues();
6565         
6566         var o = this.options;
6567         var method = this.getMethod();
6568         var isPost = method == 'POST';
6569         if(o.clientValidation === false || this.form.isValid()){
6570             
6571             if (this.form.progressUrl) {
6572                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6573                     (new Date() * 1) + '' + Math.random());
6574                     
6575             } 
6576             
6577             
6578             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6579                 form:this.form.el.dom,
6580                 url:this.getUrl(!isPost),
6581                 method: method,
6582                 params:isPost ? this.getParams() : null,
6583                 isUpload: this.form.fileUpload
6584             }));
6585             
6586             this.uploadProgress();
6587
6588         }else if (o.clientValidation !== false){ // client validation failed
6589             this.failureType = Roo.form.Action.CLIENT_INVALID;
6590             this.form.afterAction(this, false);
6591         }
6592     },
6593
6594     success : function(response)
6595     {
6596         this.uploadComplete= true;
6597         if (this.haveProgress) {
6598             Roo.MessageBox.hide();
6599         }
6600         
6601         
6602         var result = this.processResponse(response);
6603         if(result === true || result.success){
6604             this.form.afterAction(this, true);
6605             return;
6606         }
6607         if(result.errors){
6608             this.form.markInvalid(result.errors);
6609             this.failureType = Roo.form.Action.SERVER_INVALID;
6610         }
6611         this.form.afterAction(this, false);
6612     },
6613     failure : function(response)
6614     {
6615         this.uploadComplete= true;
6616         if (this.haveProgress) {
6617             Roo.MessageBox.hide();
6618         }
6619         
6620         this.response = response;
6621         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6622         this.form.afterAction(this, false);
6623     },
6624     
6625     handleResponse : function(response){
6626         if(this.form.errorReader){
6627             var rs = this.form.errorReader.read(response);
6628             var errors = [];
6629             if(rs.records){
6630                 for(var i = 0, len = rs.records.length; i < len; i++) {
6631                     var r = rs.records[i];
6632                     errors[i] = r.data;
6633                 }
6634             }
6635             if(errors.length < 1){
6636                 errors = null;
6637             }
6638             return {
6639                 success : rs.success,
6640                 errors : errors
6641             };
6642         }
6643         var ret = false;
6644         try {
6645             ret = Roo.decode(response.responseText);
6646         } catch (e) {
6647             ret = {
6648                 success: false,
6649                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6650                 errors : []
6651             };
6652         }
6653         return ret;
6654         
6655     }
6656 });
6657
6658
6659 Roo.form.Action.Load = function(form, options){
6660     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6661     this.reader = this.form.reader;
6662 };
6663
6664 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6665     type : 'load',
6666
6667     run : function(){
6668         
6669         Roo.Ajax.request(Roo.apply(
6670                 this.createCallback(), {
6671                     method:this.getMethod(),
6672                     url:this.getUrl(false),
6673                     params:this.getParams()
6674         }));
6675     },
6676
6677     success : function(response){
6678         
6679         var result = this.processResponse(response);
6680         if(result === true || !result.success || !result.data){
6681             this.failureType = Roo.form.Action.LOAD_FAILURE;
6682             this.form.afterAction(this, false);
6683             return;
6684         }
6685         this.form.clearInvalid();
6686         this.form.setValues(result.data);
6687         this.form.afterAction(this, true);
6688     },
6689
6690     handleResponse : function(response){
6691         if(this.form.reader){
6692             var rs = this.form.reader.read(response);
6693             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6694             return {
6695                 success : rs.success,
6696                 data : data
6697             };
6698         }
6699         return Roo.decode(response.responseText);
6700     }
6701 });
6702
6703 Roo.form.Action.ACTION_TYPES = {
6704     'load' : Roo.form.Action.Load,
6705     'submit' : Roo.form.Action.Submit
6706 };/*
6707  * - LGPL
6708  *
6709  * form
6710  * 
6711  */
6712
6713 /**
6714  * @class Roo.bootstrap.Form
6715  * @extends Roo.bootstrap.Component
6716  * Bootstrap Form class
6717  * @cfg {String} method  GET | POST (default POST)
6718  * @cfg {String} labelAlign top | left (default top)
6719  * @cfg {String} align left  | right - for navbars
6720  * @cfg {Boolean} loadMask load mask when submit (default true)
6721
6722  * 
6723  * @constructor
6724  * Create a new Form
6725  * @param {Object} config The config object
6726  */
6727
6728
6729 Roo.bootstrap.Form = function(config){
6730     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6731     this.addEvents({
6732         /**
6733          * @event clientvalidation
6734          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6735          * @param {Form} this
6736          * @param {Boolean} valid true if the form has passed client-side validation
6737          */
6738         clientvalidation: true,
6739         /**
6740          * @event beforeaction
6741          * Fires before any action is performed. Return false to cancel the action.
6742          * @param {Form} this
6743          * @param {Action} action The action to be performed
6744          */
6745         beforeaction: true,
6746         /**
6747          * @event actionfailed
6748          * Fires when an action fails.
6749          * @param {Form} this
6750          * @param {Action} action The action that failed
6751          */
6752         actionfailed : true,
6753         /**
6754          * @event actioncomplete
6755          * Fires when an action is completed.
6756          * @param {Form} this
6757          * @param {Action} action The action that completed
6758          */
6759         actioncomplete : true
6760     });
6761     
6762 };
6763
6764 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6765       
6766      /**
6767      * @cfg {String} method
6768      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6769      */
6770     method : 'POST',
6771     /**
6772      * @cfg {String} url
6773      * The URL to use for form actions if one isn't supplied in the action options.
6774      */
6775     /**
6776      * @cfg {Boolean} fileUpload
6777      * Set to true if this form is a file upload.
6778      */
6779      
6780     /**
6781      * @cfg {Object} baseParams
6782      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6783      */
6784       
6785     /**
6786      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6787      */
6788     timeout: 30,
6789     /**
6790      * @cfg {Sting} align (left|right) for navbar forms
6791      */
6792     align : 'left',
6793
6794     // private
6795     activeAction : null,
6796  
6797     /**
6798      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6799      * element by passing it or its id or mask the form itself by passing in true.
6800      * @type Mixed
6801      */
6802     waitMsgTarget : false,
6803     
6804     loadMask : true,
6805     
6806     getAutoCreate : function(){
6807         
6808         var cfg = {
6809             tag: 'form',
6810             method : this.method || 'POST',
6811             id : this.id || Roo.id(),
6812             cls : ''
6813         }
6814         if (this.parent().xtype.match(/^Nav/)) {
6815             cfg.cls = 'navbar-form navbar-' + this.align;
6816             
6817         }
6818         
6819         if (this.labelAlign == 'left' ) {
6820             cfg.cls += ' form-horizontal';
6821         }
6822         
6823         
6824         return cfg;
6825     },
6826     initEvents : function()
6827     {
6828         this.el.on('submit', this.onSubmit, this);
6829         // this was added as random key presses on the form where triggering form submit.
6830         this.el.on('keypress', function(e) {
6831             if (e.getCharCode() != 13) {
6832                 return true;
6833             }
6834             // we might need to allow it for textareas.. and some other items.
6835             // check e.getTarget().
6836             
6837             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6838                 return true;
6839             }
6840         
6841             Roo.log("keypress blocked");
6842             
6843             e.preventDefault();
6844             return false;
6845         });
6846         
6847     },
6848     // private
6849     onSubmit : function(e){
6850         e.stopEvent();
6851     },
6852     
6853      /**
6854      * Returns true if client-side validation on the form is successful.
6855      * @return Boolean
6856      */
6857     isValid : function(){
6858         var items = this.getItems();
6859         var valid = true;
6860         items.each(function(f){
6861            if(!f.validate()){
6862                valid = false;
6863                
6864            }
6865         });
6866         return valid;
6867     },
6868     /**
6869      * Returns true if any fields in this form have changed since their original load.
6870      * @return Boolean
6871      */
6872     isDirty : function(){
6873         var dirty = false;
6874         var items = this.getItems();
6875         items.each(function(f){
6876            if(f.isDirty()){
6877                dirty = true;
6878                return false;
6879            }
6880            return true;
6881         });
6882         return dirty;
6883     },
6884      /**
6885      * Performs a predefined action (submit or load) or custom actions you define on this form.
6886      * @param {String} actionName The name of the action type
6887      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6888      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6889      * accept other config options):
6890      * <pre>
6891 Property          Type             Description
6892 ----------------  ---------------  ----------------------------------------------------------------------------------
6893 url               String           The url for the action (defaults to the form's url)
6894 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6895 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6896 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6897                                    validate the form on the client (defaults to false)
6898      * </pre>
6899      * @return {BasicForm} this
6900      */
6901     doAction : function(action, options){
6902         if(typeof action == 'string'){
6903             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6904         }
6905         if(this.fireEvent('beforeaction', this, action) !== false){
6906             this.beforeAction(action);
6907             action.run.defer(100, action);
6908         }
6909         return this;
6910     },
6911     
6912     // private
6913     beforeAction : function(action){
6914         var o = action.options;
6915         
6916         if(this.loadMask){
6917             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6918         }
6919         // not really supported yet.. ??
6920         
6921         //if(this.waitMsgTarget === true){
6922         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6923         //}else if(this.waitMsgTarget){
6924         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6925         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6926         //}else {
6927         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6928        // }
6929          
6930     },
6931
6932     // private
6933     afterAction : function(action, success){
6934         this.activeAction = null;
6935         var o = action.options;
6936         
6937         //if(this.waitMsgTarget === true){
6938             this.el.unmask();
6939         //}else if(this.waitMsgTarget){
6940         //    this.waitMsgTarget.unmask();
6941         //}else{
6942         //    Roo.MessageBox.updateProgress(1);
6943         //    Roo.MessageBox.hide();
6944        // }
6945         // 
6946         if(success){
6947             if(o.reset){
6948                 this.reset();
6949             }
6950             Roo.callback(o.success, o.scope, [this, action]);
6951             this.fireEvent('actioncomplete', this, action);
6952             
6953         }else{
6954             
6955             // failure condition..
6956             // we have a scenario where updates need confirming.
6957             // eg. if a locking scenario exists..
6958             // we look for { errors : { needs_confirm : true }} in the response.
6959             if (
6960                 (typeof(action.result) != 'undefined')  &&
6961                 (typeof(action.result.errors) != 'undefined')  &&
6962                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6963            ){
6964                 var _t = this;
6965                 Roo.log("not supported yet");
6966                  /*
6967                 
6968                 Roo.MessageBox.confirm(
6969                     "Change requires confirmation",
6970                     action.result.errorMsg,
6971                     function(r) {
6972                         if (r != 'yes') {
6973                             return;
6974                         }
6975                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6976                     }
6977                     
6978                 );
6979                 */
6980                 
6981                 
6982                 return;
6983             }
6984             
6985             Roo.callback(o.failure, o.scope, [this, action]);
6986             // show an error message if no failed handler is set..
6987             if (!this.hasListener('actionfailed')) {
6988                 Roo.log("need to add dialog support");
6989                 /*
6990                 Roo.MessageBox.alert("Error",
6991                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6992                         action.result.errorMsg :
6993                         "Saving Failed, please check your entries or try again"
6994                 );
6995                 */
6996             }
6997             
6998             this.fireEvent('actionfailed', this, action);
6999         }
7000         
7001     },
7002     /**
7003      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7004      * @param {String} id The value to search for
7005      * @return Field
7006      */
7007     findField : function(id){
7008         var items = this.getItems();
7009         var field = items.get(id);
7010         if(!field){
7011              items.each(function(f){
7012                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7013                     field = f;
7014                     return false;
7015                 }
7016                 return true;
7017             });
7018         }
7019         return field || null;
7020     },
7021      /**
7022      * Mark fields in this form invalid in bulk.
7023      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7024      * @return {BasicForm} this
7025      */
7026     markInvalid : function(errors){
7027         if(errors instanceof Array){
7028             for(var i = 0, len = errors.length; i < len; i++){
7029                 var fieldError = errors[i];
7030                 var f = this.findField(fieldError.id);
7031                 if(f){
7032                     f.markInvalid(fieldError.msg);
7033                 }
7034             }
7035         }else{
7036             var field, id;
7037             for(id in errors){
7038                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7039                     field.markInvalid(errors[id]);
7040                 }
7041             }
7042         }
7043         //Roo.each(this.childForms || [], function (f) {
7044         //    f.markInvalid(errors);
7045         //});
7046         
7047         return this;
7048     },
7049
7050     /**
7051      * Set values for fields in this form in bulk.
7052      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7053      * @return {BasicForm} this
7054      */
7055     setValues : function(values){
7056         if(values instanceof Array){ // array of objects
7057             for(var i = 0, len = values.length; i < len; i++){
7058                 var v = values[i];
7059                 var f = this.findField(v.id);
7060                 if(f){
7061                     f.setValue(v.value);
7062                     if(this.trackResetOnLoad){
7063                         f.originalValue = f.getValue();
7064                     }
7065                 }
7066             }
7067         }else{ // object hash
7068             var field, id;
7069             for(id in values){
7070                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7071                     
7072                     if (field.setFromData && 
7073                         field.valueField && 
7074                         field.displayField &&
7075                         // combos' with local stores can 
7076                         // be queried via setValue()
7077                         // to set their value..
7078                         (field.store && !field.store.isLocal)
7079                         ) {
7080                         // it's a combo
7081                         var sd = { };
7082                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7083                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7084                         field.setFromData(sd);
7085                         
7086                     } else {
7087                         field.setValue(values[id]);
7088                     }
7089                     
7090                     
7091                     if(this.trackResetOnLoad){
7092                         field.originalValue = field.getValue();
7093                     }
7094                 }
7095             }
7096         }
7097          
7098         //Roo.each(this.childForms || [], function (f) {
7099         //    f.setValues(values);
7100         //});
7101                 
7102         return this;
7103     },
7104
7105     /**
7106      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7107      * they are returned as an array.
7108      * @param {Boolean} asString
7109      * @return {Object}
7110      */
7111     getValues : function(asString){
7112         //if (this.childForms) {
7113             // copy values from the child forms
7114         //    Roo.each(this.childForms, function (f) {
7115         //        this.setValues(f.getValues());
7116         //    }, this);
7117         //}
7118         
7119         
7120         
7121         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7122         if(asString === true){
7123             return fs;
7124         }
7125         return Roo.urlDecode(fs);
7126     },
7127     
7128     /**
7129      * Returns the fields in this form as an object with key/value pairs. 
7130      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7131      * @return {Object}
7132      */
7133     getFieldValues : function(with_hidden)
7134     {
7135         var items = this.getItems();
7136         var ret = {};
7137         items.each(function(f){
7138             if (!f.getName()) {
7139                 return;
7140             }
7141             var v = f.getValue();
7142             if (f.inputType =='radio') {
7143                 if (typeof(ret[f.getName()]) == 'undefined') {
7144                     ret[f.getName()] = ''; // empty..
7145                 }
7146                 
7147                 if (!f.el.dom.checked) {
7148                     return;
7149                     
7150                 }
7151                 v = f.el.dom.value;
7152                 
7153             }
7154             
7155             // not sure if this supported any more..
7156             if ((typeof(v) == 'object') && f.getRawValue) {
7157                 v = f.getRawValue() ; // dates..
7158             }
7159             // combo boxes where name != hiddenName...
7160             if (f.name != f.getName()) {
7161                 ret[f.name] = f.getRawValue();
7162             }
7163             ret[f.getName()] = v;
7164         });
7165         
7166         return ret;
7167     },
7168
7169     /**
7170      * Clears all invalid messages in this form.
7171      * @return {BasicForm} this
7172      */
7173     clearInvalid : function(){
7174         var items = this.getItems();
7175         
7176         items.each(function(f){
7177            f.clearInvalid();
7178         });
7179         
7180         
7181         
7182         return this;
7183     },
7184
7185     /**
7186      * Resets this form.
7187      * @return {BasicForm} this
7188      */
7189     reset : function(){
7190         var items = this.getItems();
7191         items.each(function(f){
7192             f.reset();
7193         });
7194         
7195         Roo.each(this.childForms || [], function (f) {
7196             f.reset();
7197         });
7198        
7199         
7200         return this;
7201     },
7202     getItems : function()
7203     {
7204         var r=new Roo.util.MixedCollection(false, function(o){
7205             return o.id || (o.id = Roo.id());
7206         });
7207         var iter = function(el) {
7208             if (el.inputEl) {
7209                 r.add(el);
7210             }
7211             if (!el.items) {
7212                 return;
7213             }
7214             Roo.each(el.items,function(e) {
7215                 iter(e);
7216             });
7217             
7218             
7219         };
7220         
7221         iter(this);
7222         return r;
7223         
7224         
7225         
7226         
7227     }
7228     
7229 });
7230
7231  
7232 /*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242 /**
7243  * @class Roo.form.VTypes
7244  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7245  * @singleton
7246  */
7247 Roo.form.VTypes = function(){
7248     // closure these in so they are only created once.
7249     var alpha = /^[a-zA-Z_]+$/;
7250     var alphanum = /^[a-zA-Z0-9_]+$/;
7251     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7252     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7253
7254     // All these messages and functions are configurable
7255     return {
7256         /**
7257          * The function used to validate email addresses
7258          * @param {String} value The email address
7259          */
7260         'email' : function(v){
7261             return email.test(v);
7262         },
7263         /**
7264          * The error text to display when the email validation function returns false
7265          * @type String
7266          */
7267         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7268         /**
7269          * The keystroke filter mask to be applied on email input
7270          * @type RegExp
7271          */
7272         'emailMask' : /[a-z0-9_\.\-@]/i,
7273
7274         /**
7275          * The function used to validate URLs
7276          * @param {String} value The URL
7277          */
7278         'url' : function(v){
7279             return url.test(v);
7280         },
7281         /**
7282          * The error text to display when the url validation function returns false
7283          * @type String
7284          */
7285         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7286         
7287         /**
7288          * The function used to validate alpha values
7289          * @param {String} value The value
7290          */
7291         'alpha' : function(v){
7292             return alpha.test(v);
7293         },
7294         /**
7295          * The error text to display when the alpha validation function returns false
7296          * @type String
7297          */
7298         'alphaText' : 'This field should only contain letters and _',
7299         /**
7300          * The keystroke filter mask to be applied on alpha input
7301          * @type RegExp
7302          */
7303         'alphaMask' : /[a-z_]/i,
7304
7305         /**
7306          * The function used to validate alphanumeric values
7307          * @param {String} value The value
7308          */
7309         'alphanum' : function(v){
7310             return alphanum.test(v);
7311         },
7312         /**
7313          * The error text to display when the alphanumeric validation function returns false
7314          * @type String
7315          */
7316         'alphanumText' : 'This field should only contain letters, numbers and _',
7317         /**
7318          * The keystroke filter mask to be applied on alphanumeric input
7319          * @type RegExp
7320          */
7321         'alphanumMask' : /[a-z0-9_]/i
7322     };
7323 }();/*
7324  * - LGPL
7325  *
7326  * Input
7327  * 
7328  */
7329
7330 /**
7331  * @class Roo.bootstrap.Input
7332  * @extends Roo.bootstrap.Component
7333  * Bootstrap Input class
7334  * @cfg {Boolean} disabled is it disabled
7335  * @cfg {String} fieldLabel - the label associated
7336  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7337  * @cfg {String} name name of the input
7338  * @cfg {string} fieldLabel - the label associated
7339  * @cfg {string}  inputType - input / file submit ...
7340  * @cfg {string} placeholder - placeholder to put in text.
7341  * @cfg {string}  before - input group add on before
7342  * @cfg {string} after - input group add on after
7343  * @cfg {string} size - (lg|sm) or leave empty..
7344  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7345  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7346  * @cfg {Number} md colspan out of 12 for computer-sized screens
7347  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7348  * @cfg {string} value default value of the input
7349  * @cfg {Number} labelWidth set the width of label (0-12)
7350  * @cfg {String} labelAlign (top|left)
7351  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7352  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7353
7354  * @cfg {String} align (left|center|right) Default left
7355  * 
7356  * 
7357  * 
7358  * @constructor
7359  * Create a new Input
7360  * @param {Object} config The config object
7361  */
7362
7363 Roo.bootstrap.Input = function(config){
7364     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7365    
7366         this.addEvents({
7367             /**
7368              * @event focus
7369              * Fires when this field receives input focus.
7370              * @param {Roo.form.Field} this
7371              */
7372             focus : true,
7373             /**
7374              * @event blur
7375              * Fires when this field loses input focus.
7376              * @param {Roo.form.Field} this
7377              */
7378             blur : true,
7379             /**
7380              * @event specialkey
7381              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7382              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7383              * @param {Roo.form.Field} this
7384              * @param {Roo.EventObject} e The event object
7385              */
7386             specialkey : true,
7387             /**
7388              * @event change
7389              * Fires just before the field blurs if the field value has changed.
7390              * @param {Roo.form.Field} this
7391              * @param {Mixed} newValue The new value
7392              * @param {Mixed} oldValue The original value
7393              */
7394             change : true,
7395             /**
7396              * @event invalid
7397              * Fires after the field has been marked as invalid.
7398              * @param {Roo.form.Field} this
7399              * @param {String} msg The validation message
7400              */
7401             invalid : true,
7402             /**
7403              * @event valid
7404              * Fires after the field has been validated with no errors.
7405              * @param {Roo.form.Field} this
7406              */
7407             valid : true,
7408              /**
7409              * @event keyup
7410              * Fires after the key up
7411              * @param {Roo.form.Field} this
7412              * @param {Roo.EventObject}  e The event Object
7413              */
7414             keyup : true
7415         });
7416 };
7417
7418 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7419      /**
7420      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7421       automatic validation (defaults to "keyup").
7422      */
7423     validationEvent : "keyup",
7424      /**
7425      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7426      */
7427     validateOnBlur : true,
7428     /**
7429      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7430      */
7431     validationDelay : 250,
7432      /**
7433      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7434      */
7435     focusClass : "x-form-focus",  // not needed???
7436     
7437        
7438     /**
7439      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7440      */
7441     invalidClass : "has-warning",
7442     
7443     /**
7444      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7445      */
7446     validClass : "has-success",
7447     
7448     /**
7449      * @cfg {Boolean} hasFeedback (true|false) default true
7450      */
7451     hasFeedback : true,
7452     
7453     /**
7454      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7455      */
7456     invalidFeedbackClass : "glyphicon-warning-sign",
7457     
7458     /**
7459      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7460      */
7461     validFeedbackClass : "glyphicon-ok",
7462     
7463     /**
7464      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7465      */
7466     selectOnFocus : false,
7467     
7468      /**
7469      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7470      */
7471     maskRe : null,
7472        /**
7473      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7474      */
7475     vtype : null,
7476     
7477       /**
7478      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7479      */
7480     disableKeyFilter : false,
7481     
7482        /**
7483      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7484      */
7485     disabled : false,
7486      /**
7487      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7488      */
7489     allowBlank : true,
7490     /**
7491      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7492      */
7493     blankText : "This field is required",
7494     
7495      /**
7496      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7497      */
7498     minLength : 0,
7499     /**
7500      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7501      */
7502     maxLength : Number.MAX_VALUE,
7503     /**
7504      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7505      */
7506     minLengthText : "The minimum length for this field is {0}",
7507     /**
7508      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7509      */
7510     maxLengthText : "The maximum length for this field is {0}",
7511   
7512     
7513     /**
7514      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7515      * If available, this function will be called only after the basic validators all return true, and will be passed the
7516      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7517      */
7518     validator : null,
7519     /**
7520      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7521      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7522      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7523      */
7524     regex : null,
7525     /**
7526      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7527      */
7528     regexText : "",
7529     
7530     autocomplete: false,
7531     
7532     
7533     fieldLabel : '',
7534     inputType : 'text',
7535     
7536     name : false,
7537     placeholder: false,
7538     before : false,
7539     after : false,
7540     size : false,
7541     hasFocus : false,
7542     preventMark: false,
7543     isFormField : true,
7544     value : '',
7545     labelWidth : 2,
7546     labelAlign : false,
7547     readOnly : false,
7548     align : false,
7549     formatedValue : false,
7550     
7551     parentLabelAlign : function()
7552     {
7553         var parent = this;
7554         while (parent.parent()) {
7555             parent = parent.parent();
7556             if (typeof(parent.labelAlign) !='undefined') {
7557                 return parent.labelAlign;
7558             }
7559         }
7560         return 'left';
7561         
7562     },
7563     
7564     getAutoCreate : function(){
7565         
7566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7567         
7568         var id = Roo.id();
7569         
7570         var cfg = {};
7571         
7572         if(this.inputType != 'hidden'){
7573             cfg.cls = 'form-group' //input-group
7574         }
7575         
7576         var input =  {
7577             tag: 'input',
7578             id : id,
7579             type : this.inputType,
7580             value : this.value,
7581             cls : 'form-control',
7582             placeholder : this.placeholder || '',
7583             autocomplete : this.autocomplete || 'new-password'
7584         };
7585         
7586         
7587         if(this.align){
7588             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7589         }
7590         
7591         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7592             input.maxLength = this.maxLength;
7593         }
7594         
7595         if (this.disabled) {
7596             input.disabled=true;
7597         }
7598         
7599         if (this.readOnly) {
7600             input.readonly=true;
7601         }
7602         
7603         if (this.name) {
7604             input.name = this.name;
7605         }
7606         if (this.size) {
7607             input.cls += ' input-' + this.size;
7608         }
7609         var settings=this;
7610         ['xs','sm','md','lg'].map(function(size){
7611             if (settings[size]) {
7612                 cfg.cls += ' col-' + size + '-' + settings[size];
7613             }
7614         });
7615         
7616         var inputblock = input;
7617         
7618         var feedback = {
7619             tag: 'span',
7620             cls: 'glyphicon form-control-feedback'
7621         };
7622             
7623         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7624             
7625             inputblock = {
7626                 cls : 'has-feedback',
7627                 cn :  [
7628                     input,
7629                     feedback
7630                 ] 
7631             };  
7632         }
7633         
7634         if (this.before || this.after) {
7635             
7636             inputblock = {
7637                 cls : 'input-group',
7638                 cn :  [] 
7639             };
7640             
7641             if (this.before && typeof(this.before) == 'string') {
7642                 
7643                 inputblock.cn.push({
7644                     tag :'span',
7645                     cls : 'roo-input-before input-group-addon',
7646                     html : this.before
7647                 });
7648             }
7649             if (this.before && typeof(this.before) == 'object') {
7650                 this.before = Roo.factory(this.before);
7651                 Roo.log(this.before);
7652                 inputblock.cn.push({
7653                     tag :'span',
7654                     cls : 'roo-input-before input-group-' +
7655                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7656                 });
7657             }
7658             
7659             inputblock.cn.push(input);
7660             
7661             if (this.after && typeof(this.after) == 'string') {
7662                 inputblock.cn.push({
7663                     tag :'span',
7664                     cls : 'roo-input-after input-group-addon',
7665                     html : this.after
7666                 });
7667             }
7668             if (this.after && typeof(this.after) == 'object') {
7669                 this.after = Roo.factory(this.after);
7670                 Roo.log(this.after);
7671                 inputblock.cn.push({
7672                     tag :'span',
7673                     cls : 'roo-input-after input-group-' +
7674                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7675                 });
7676             }
7677             
7678             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7679                 inputblock.cls += ' has-feedback';
7680                 inputblock.cn.push(feedback);
7681             }
7682         };
7683         
7684         if (align ==='left' && this.fieldLabel.length) {
7685                 Roo.log("left and has label");
7686                 cfg.cn = [
7687                     
7688                     {
7689                         tag: 'label',
7690                         'for' :  id,
7691                         cls : 'control-label col-sm-' + this.labelWidth,
7692                         html : this.fieldLabel
7693                         
7694                     },
7695                     {
7696                         cls : "col-sm-" + (12 - this.labelWidth), 
7697                         cn: [
7698                             inputblock
7699                         ]
7700                     }
7701                     
7702                 ];
7703         } else if ( this.fieldLabel.length) {
7704                 Roo.log(" label");
7705                  cfg.cn = [
7706                    
7707                     {
7708                         tag: 'label',
7709                         //cls : 'input-group-addon',
7710                         html : this.fieldLabel
7711                         
7712                     },
7713                     
7714                     inputblock
7715                     
7716                 ];
7717
7718         } else {
7719             
7720                 Roo.log(" no label && no align");
7721                 cfg.cn = [
7722                     
7723                         inputblock
7724                     
7725                 ];
7726                 
7727                 
7728         };
7729         Roo.log('input-parentType: ' + this.parentType);
7730         
7731         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7732            cfg.cls += ' navbar-form';
7733            Roo.log(cfg);
7734         }
7735         
7736         return cfg;
7737         
7738     },
7739     /**
7740      * return the real input element.
7741      */
7742     inputEl: function ()
7743     {
7744         return this.el.select('input.form-control',true).first();
7745     },
7746     
7747     tooltipEl : function()
7748     {
7749         return this.inputEl();
7750     },
7751     
7752     setDisabled : function(v)
7753     {
7754         var i  = this.inputEl().dom;
7755         if (!v) {
7756             i.removeAttribute('disabled');
7757             return;
7758             
7759         }
7760         i.setAttribute('disabled','true');
7761     },
7762     initEvents : function()
7763     {
7764           
7765         this.inputEl().on("keydown" , this.fireKey,  this);
7766         this.inputEl().on("focus", this.onFocus,  this);
7767         this.inputEl().on("blur", this.onBlur,  this);
7768         
7769         this.inputEl().relayEvent('keyup', this);
7770
7771         // reference to original value for reset
7772         this.originalValue = this.getValue();
7773         //Roo.form.TextField.superclass.initEvents.call(this);
7774         if(this.validationEvent == 'keyup'){
7775             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7776             this.inputEl().on('keyup', this.filterValidation, this);
7777         }
7778         else if(this.validationEvent !== false){
7779             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7780         }
7781         
7782         if(this.selectOnFocus){
7783             this.on("focus", this.preFocus, this);
7784             
7785         }
7786         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7787             this.inputEl().on("keypress", this.filterKeys, this);
7788         }
7789        /* if(this.grow){
7790             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7791             this.el.on("click", this.autoSize,  this);
7792         }
7793         */
7794         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7795             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7796         }
7797         
7798         if (typeof(this.before) == 'object') {
7799             this.before.render(this.el.select('.roo-input-before',true).first());
7800         }
7801         if (typeof(this.after) == 'object') {
7802             this.after.render(this.el.select('.roo-input-after',true).first());
7803         }
7804         
7805         
7806     },
7807     filterValidation : function(e){
7808         if(!e.isNavKeyPress()){
7809             this.validationTask.delay(this.validationDelay);
7810         }
7811     },
7812      /**
7813      * Validates the field value
7814      * @return {Boolean} True if the value is valid, else false
7815      */
7816     validate : function(){
7817         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7818         if(this.disabled || this.validateValue(this.getRawValue())){
7819             this.markValid();
7820             return true;
7821         }
7822         
7823         this.markInvalid();
7824         return false;
7825     },
7826     
7827     
7828     /**
7829      * Validates a value according to the field's validation rules and marks the field as invalid
7830      * if the validation fails
7831      * @param {Mixed} value The value to validate
7832      * @return {Boolean} True if the value is valid, else false
7833      */
7834     validateValue : function(value){
7835         if(value.length < 1)  { // if it's blank
7836             if(this.allowBlank){
7837                 return true;
7838             }
7839             return false;
7840         }
7841         
7842         if(value.length < this.minLength){
7843             return false;
7844         }
7845         if(value.length > this.maxLength){
7846             return false;
7847         }
7848         if(this.vtype){
7849             var vt = Roo.form.VTypes;
7850             if(!vt[this.vtype](value, this)){
7851                 return false;
7852             }
7853         }
7854         if(typeof this.validator == "function"){
7855             var msg = this.validator(value);
7856             if(msg !== true){
7857                 return false;
7858             }
7859         }
7860         
7861         if(this.regex && !this.regex.test(value)){
7862             return false;
7863         }
7864         
7865         return true;
7866     },
7867
7868     
7869     
7870      // private
7871     fireKey : function(e){
7872         //Roo.log('field ' + e.getKey());
7873         if(e.isNavKeyPress()){
7874             this.fireEvent("specialkey", this, e);
7875         }
7876     },
7877     focus : function (selectText){
7878         if(this.rendered){
7879             this.inputEl().focus();
7880             if(selectText === true){
7881                 this.inputEl().dom.select();
7882             }
7883         }
7884         return this;
7885     } ,
7886     
7887     onFocus : function(){
7888         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7889            // this.el.addClass(this.focusClass);
7890         }
7891         if(!this.hasFocus){
7892             this.hasFocus = true;
7893             this.startValue = this.getValue();
7894             this.fireEvent("focus", this);
7895         }
7896     },
7897     
7898     beforeBlur : Roo.emptyFn,
7899
7900     
7901     // private
7902     onBlur : function(){
7903         this.beforeBlur();
7904         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7905             //this.el.removeClass(this.focusClass);
7906         }
7907         this.hasFocus = false;
7908         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7909             this.validate();
7910         }
7911         var v = this.getValue();
7912         if(String(v) !== String(this.startValue)){
7913             this.fireEvent('change', this, v, this.startValue);
7914         }
7915         this.fireEvent("blur", this);
7916     },
7917     
7918     /**
7919      * Resets the current field value to the originally loaded value and clears any validation messages
7920      */
7921     reset : function(){
7922         this.setValue(this.originalValue);
7923         this.validate();
7924     },
7925      /**
7926      * Returns the name of the field
7927      * @return {Mixed} name The name field
7928      */
7929     getName: function(){
7930         return this.name;
7931     },
7932      /**
7933      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7934      * @return {Mixed} value The field value
7935      */
7936     getValue : function(){
7937         
7938         var v = this.inputEl().getValue();
7939         
7940         return v;
7941     },
7942     /**
7943      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7944      * @return {Mixed} value The field value
7945      */
7946     getRawValue : function(){
7947         var v = this.inputEl().getValue();
7948         
7949         return v;
7950     },
7951     
7952     /**
7953      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7954      * @param {Mixed} value The value to set
7955      */
7956     setRawValue : function(v){
7957         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7958     },
7959     
7960     selectText : function(start, end){
7961         var v = this.getRawValue();
7962         if(v.length > 0){
7963             start = start === undefined ? 0 : start;
7964             end = end === undefined ? v.length : end;
7965             var d = this.inputEl().dom;
7966             if(d.setSelectionRange){
7967                 d.setSelectionRange(start, end);
7968             }else if(d.createTextRange){
7969                 var range = d.createTextRange();
7970                 range.moveStart("character", start);
7971                 range.moveEnd("character", v.length-end);
7972                 range.select();
7973             }
7974         }
7975     },
7976     
7977     /**
7978      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7979      * @param {Mixed} value The value to set
7980      */
7981     setValue : function(v){
7982         this.value = v;
7983         if(this.rendered){
7984             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7985             this.validate();
7986         }
7987     },
7988     
7989     /*
7990     processValue : function(value){
7991         if(this.stripCharsRe){
7992             var newValue = value.replace(this.stripCharsRe, '');
7993             if(newValue !== value){
7994                 this.setRawValue(newValue);
7995                 return newValue;
7996             }
7997         }
7998         return value;
7999     },
8000   */
8001     preFocus : function(){
8002         
8003         if(this.selectOnFocus){
8004             this.inputEl().dom.select();
8005         }
8006     },
8007     filterKeys : function(e){
8008         var k = e.getKey();
8009         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8010             return;
8011         }
8012         var c = e.getCharCode(), cc = String.fromCharCode(c);
8013         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8014             return;
8015         }
8016         if(!this.maskRe.test(cc)){
8017             e.stopEvent();
8018         }
8019     },
8020      /**
8021      * Clear any invalid styles/messages for this field
8022      */
8023     clearInvalid : function(){
8024         
8025         if(!this.el || this.preventMark){ // not rendered
8026             return;
8027         }
8028         this.el.removeClass(this.invalidClass);
8029         
8030         this.fireEvent('valid', this);
8031     },
8032     
8033      /**
8034      * Mark this field as valid
8035      */
8036     markValid : function(){
8037         if(!this.el  || this.preventMark){ // not rendered
8038             return;
8039         }
8040         
8041         this.el.removeClass([this.invalidClass, this.validClass]);
8042         
8043         if(this.disabled || this.allowBlank){
8044             return;
8045         }
8046         
8047         this.el.addClass(this.validClass);
8048         
8049         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8050             
8051             var feedback = this.el.select('.form-control-feedback', true).first();
8052             
8053             if(feedback){
8054                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8055                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8056             }
8057             
8058         }
8059         
8060         this.fireEvent('valid', this);
8061     },
8062     
8063      /**
8064      * Mark this field as invalid
8065      * @param {String} msg The validation message
8066      */
8067     markInvalid : function(msg){
8068         if(!this.el  || this.preventMark){ // not rendered
8069             return;
8070         }
8071         
8072         this.el.removeClass([this.invalidClass, this.validClass]);
8073         
8074         if(this.disabled || this.allowBlank){
8075             return;
8076         }
8077         
8078         this.el.addClass(this.invalidClass);
8079         
8080         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8081             
8082             var feedback = this.el.select('.form-control-feedback', true).first();
8083             
8084             if(feedback){
8085                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8086                 
8087                 if(this.getValue().length){
8088                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8089                 }
8090                 
8091             }
8092             
8093         }
8094         
8095         this.fireEvent('invalid', this, msg);
8096     },
8097     // private
8098     SafariOnKeyDown : function(event)
8099     {
8100         // this is a workaround for a password hang bug on chrome/ webkit.
8101         
8102         var isSelectAll = false;
8103         
8104         if(this.inputEl().dom.selectionEnd > 0){
8105             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8106         }
8107         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8108             event.preventDefault();
8109             this.setValue('');
8110             return;
8111         }
8112         
8113         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8114             
8115             event.preventDefault();
8116             // this is very hacky as keydown always get's upper case.
8117             //
8118             var cc = String.fromCharCode(event.getCharCode());
8119             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8120             
8121         }
8122     },
8123     adjustWidth : function(tag, w){
8124         tag = tag.toLowerCase();
8125         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8126             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8127                 if(tag == 'input'){
8128                     return w + 2;
8129                 }
8130                 if(tag == 'textarea'){
8131                     return w-2;
8132                 }
8133             }else if(Roo.isOpera){
8134                 if(tag == 'input'){
8135                     return w + 2;
8136                 }
8137                 if(tag == 'textarea'){
8138                     return w-2;
8139                 }
8140             }
8141         }
8142         return w;
8143     }
8144     
8145 });
8146
8147  
8148 /*
8149  * - LGPL
8150  *
8151  * Input
8152  * 
8153  */
8154
8155 /**
8156  * @class Roo.bootstrap.TextArea
8157  * @extends Roo.bootstrap.Input
8158  * Bootstrap TextArea class
8159  * @cfg {Number} cols Specifies the visible width of a text area
8160  * @cfg {Number} rows Specifies the visible number of lines in a text area
8161  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8162  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8163  * @cfg {string} html text
8164  * 
8165  * @constructor
8166  * Create a new TextArea
8167  * @param {Object} config The config object
8168  */
8169
8170 Roo.bootstrap.TextArea = function(config){
8171     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8172    
8173 };
8174
8175 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8176      
8177     cols : false,
8178     rows : 5,
8179     readOnly : false,
8180     warp : 'soft',
8181     resize : false,
8182     value: false,
8183     html: false,
8184     
8185     getAutoCreate : function(){
8186         
8187         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8188         
8189         var id = Roo.id();
8190         
8191         var cfg = {};
8192         
8193         var input =  {
8194             tag: 'textarea',
8195             id : id,
8196             warp : this.warp,
8197             rows : this.rows,
8198             value : this.value || '',
8199             html: this.html || '',
8200             cls : 'form-control',
8201             placeholder : this.placeholder || '' 
8202             
8203         };
8204         
8205         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8206             input.maxLength = this.maxLength;
8207         }
8208         
8209         if(this.resize){
8210             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8211         }
8212         
8213         if(this.cols){
8214             input.cols = this.cols;
8215         }
8216         
8217         if (this.readOnly) {
8218             input.readonly = true;
8219         }
8220         
8221         if (this.name) {
8222             input.name = this.name;
8223         }
8224         
8225         if (this.size) {
8226             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8227         }
8228         
8229         var settings=this;
8230         ['xs','sm','md','lg'].map(function(size){
8231             if (settings[size]) {
8232                 cfg.cls += ' col-' + size + '-' + settings[size];
8233             }
8234         });
8235         
8236         var inputblock = input;
8237         
8238         if(this.hasFeedback && !this.allowBlank){
8239             
8240             var feedback = {
8241                 tag: 'span',
8242                 cls: 'glyphicon form-control-feedback'
8243             };
8244
8245             inputblock = {
8246                 cls : 'has-feedback',
8247                 cn :  [
8248                     input,
8249                     feedback
8250                 ] 
8251             };  
8252         }
8253         
8254         
8255         if (this.before || this.after) {
8256             
8257             inputblock = {
8258                 cls : 'input-group',
8259                 cn :  [] 
8260             };
8261             if (this.before) {
8262                 inputblock.cn.push({
8263                     tag :'span',
8264                     cls : 'input-group-addon',
8265                     html : this.before
8266                 });
8267             }
8268             
8269             inputblock.cn.push(input);
8270             
8271             if(this.hasFeedback && !this.allowBlank){
8272                 inputblock.cls += ' has-feedback';
8273                 inputblock.cn.push(feedback);
8274             }
8275             
8276             if (this.after) {
8277                 inputblock.cn.push({
8278                     tag :'span',
8279                     cls : 'input-group-addon',
8280                     html : this.after
8281                 });
8282             }
8283             
8284         }
8285         
8286         if (align ==='left' && this.fieldLabel.length) {
8287                 Roo.log("left and has label");
8288                 cfg.cn = [
8289                     
8290                     {
8291                         tag: 'label',
8292                         'for' :  id,
8293                         cls : 'control-label col-sm-' + this.labelWidth,
8294                         html : this.fieldLabel
8295                         
8296                     },
8297                     {
8298                         cls : "col-sm-" + (12 - this.labelWidth), 
8299                         cn: [
8300                             inputblock
8301                         ]
8302                     }
8303                     
8304                 ];
8305         } else if ( this.fieldLabel.length) {
8306                 Roo.log(" label");
8307                  cfg.cn = [
8308                    
8309                     {
8310                         tag: 'label',
8311                         //cls : 'input-group-addon',
8312                         html : this.fieldLabel
8313                         
8314                     },
8315                     
8316                     inputblock
8317                     
8318                 ];
8319
8320         } else {
8321             
8322                    Roo.log(" no label && no align");
8323                 cfg.cn = [
8324                     
8325                         inputblock
8326                     
8327                 ];
8328                 
8329                 
8330         }
8331         
8332         if (this.disabled) {
8333             input.disabled=true;
8334         }
8335         
8336         return cfg;
8337         
8338     },
8339     /**
8340      * return the real textarea element.
8341      */
8342     inputEl: function ()
8343     {
8344         return this.el.select('textarea.form-control',true).first();
8345     }
8346 });
8347
8348  
8349 /*
8350  * - LGPL
8351  *
8352  * trigger field - base class for combo..
8353  * 
8354  */
8355  
8356 /**
8357  * @class Roo.bootstrap.TriggerField
8358  * @extends Roo.bootstrap.Input
8359  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8360  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8361  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8362  * for which you can provide a custom implementation.  For example:
8363  * <pre><code>
8364 var trigger = new Roo.bootstrap.TriggerField();
8365 trigger.onTriggerClick = myTriggerFn;
8366 trigger.applyTo('my-field');
8367 </code></pre>
8368  *
8369  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8370  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8371  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8372  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8373  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8374
8375  * @constructor
8376  * Create a new TriggerField.
8377  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8378  * to the base TextField)
8379  */
8380 Roo.bootstrap.TriggerField = function(config){
8381     this.mimicing = false;
8382     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8383 };
8384
8385 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8386     /**
8387      * @cfg {String} triggerClass A CSS class to apply to the trigger
8388      */
8389      /**
8390      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8391      */
8392     hideTrigger:false,
8393
8394     /** @cfg {Boolean} grow @hide */
8395     /** @cfg {Number} growMin @hide */
8396     /** @cfg {Number} growMax @hide */
8397
8398     /**
8399      * @hide 
8400      * @method
8401      */
8402     autoSize: Roo.emptyFn,
8403     // private
8404     monitorTab : true,
8405     // private
8406     deferHeight : true,
8407
8408     
8409     actionMode : 'wrap',
8410     
8411     caret : false,
8412     
8413     
8414     getAutoCreate : function(){
8415        
8416         var align = this.labelAlign || this.parentLabelAlign();
8417         
8418         var id = Roo.id();
8419         
8420         var cfg = {
8421             cls: 'form-group' //input-group
8422         };
8423         
8424         
8425         var input =  {
8426             tag: 'input',
8427             id : id,
8428             type : this.inputType,
8429             cls : 'form-control',
8430             autocomplete: 'new-password',
8431             placeholder : this.placeholder || '' 
8432             
8433         };
8434         if (this.name) {
8435             input.name = this.name;
8436         }
8437         if (this.size) {
8438             input.cls += ' input-' + this.size;
8439         }
8440         
8441         if (this.disabled) {
8442             input.disabled=true;
8443         }
8444         
8445         var inputblock = input;
8446         
8447         if(this.hasFeedback && !this.allowBlank){
8448             
8449             var feedback = {
8450                 tag: 'span',
8451                 cls: 'glyphicon form-control-feedback'
8452             };
8453
8454             inputblock = {
8455                 cls : 'has-feedback',
8456                 cn :  [
8457                     input,
8458                     feedback
8459                 ] 
8460             };  
8461         }
8462         
8463         if (this.before || this.after) {
8464             
8465             inputblock = {
8466                 cls : 'input-group',
8467                 cn :  [] 
8468             };
8469             if (this.before) {
8470                 inputblock.cn.push({
8471                     tag :'span',
8472                     cls : 'input-group-addon',
8473                     html : this.before
8474                 });
8475             }
8476             
8477             inputblock.cn.push(input);
8478             
8479             if(this.hasFeedback && !this.allowBlank){
8480                 inputblock.cls += ' has-feedback';
8481                 inputblock.cn.push(feedback);
8482             }
8483             
8484             if (this.after) {
8485                 inputblock.cn.push({
8486                     tag :'span',
8487                     cls : 'input-group-addon',
8488                     html : this.after
8489                 });
8490             }
8491             
8492         };
8493         
8494         var box = {
8495             tag: 'div',
8496             cn: [
8497                 {
8498                     tag: 'input',
8499                     type : 'hidden',
8500                     cls: 'form-hidden-field'
8501                 },
8502                 inputblock
8503             ]
8504             
8505         };
8506         
8507         if(this.multiple){
8508             Roo.log('multiple');
8509             
8510             box = {
8511                 tag: 'div',
8512                 cn: [
8513                     {
8514                         tag: 'input',
8515                         type : 'hidden',
8516                         cls: 'form-hidden-field'
8517                     },
8518                     {
8519                         tag: 'ul',
8520                         cls: 'select2-choices',
8521                         cn:[
8522                             {
8523                                 tag: 'li',
8524                                 cls: 'select2-search-field',
8525                                 cn: [
8526
8527                                     inputblock
8528                                 ]
8529                             }
8530                         ]
8531                     }
8532                 ]
8533             }
8534         };
8535         
8536         var combobox = {
8537             cls: 'select2-container input-group',
8538             cn: [
8539                 box
8540 //                {
8541 //                    tag: 'ul',
8542 //                    cls: 'typeahead typeahead-long dropdown-menu',
8543 //                    style: 'display:none'
8544 //                }
8545             ]
8546         };
8547         
8548         if(!this.multiple && this.showToggleBtn){
8549             
8550             var caret = {
8551                         tag: 'span',
8552                         cls: 'caret'
8553              };
8554             if (this.caret != false) {
8555                 caret = {
8556                      tag: 'i',
8557                      cls: 'fa fa-' + this.caret
8558                 };
8559                 
8560             }
8561             
8562             combobox.cn.push({
8563                 tag :'span',
8564                 cls : 'input-group-addon btn dropdown-toggle',
8565                 cn : [
8566                     caret,
8567                     {
8568                         tag: 'span',
8569                         cls: 'combobox-clear',
8570                         cn  : [
8571                             {
8572                                 tag : 'i',
8573                                 cls: 'icon-remove'
8574                             }
8575                         ]
8576                     }
8577                 ]
8578
8579             })
8580         }
8581         
8582         if(this.multiple){
8583             combobox.cls += ' select2-container-multi';
8584         }
8585         
8586         if (align ==='left' && this.fieldLabel.length) {
8587             
8588                 Roo.log("left and has label");
8589                 cfg.cn = [
8590                     
8591                     {
8592                         tag: 'label',
8593                         'for' :  id,
8594                         cls : 'control-label col-sm-' + this.labelWidth,
8595                         html : this.fieldLabel
8596                         
8597                     },
8598                     {
8599                         cls : "col-sm-" + (12 - this.labelWidth), 
8600                         cn: [
8601                             combobox
8602                         ]
8603                     }
8604                     
8605                 ];
8606         } else if ( this.fieldLabel.length) {
8607                 Roo.log(" label");
8608                  cfg.cn = [
8609                    
8610                     {
8611                         tag: 'label',
8612                         //cls : 'input-group-addon',
8613                         html : this.fieldLabel
8614                         
8615                     },
8616                     
8617                     combobox
8618                     
8619                 ];
8620
8621         } else {
8622             
8623                 Roo.log(" no label && no align");
8624                 cfg = combobox
8625                      
8626                 
8627         }
8628          
8629         var settings=this;
8630         ['xs','sm','md','lg'].map(function(size){
8631             if (settings[size]) {
8632                 cfg.cls += ' col-' + size + '-' + settings[size];
8633             }
8634         });
8635         
8636         return cfg;
8637         
8638     },
8639     
8640     
8641     
8642     // private
8643     onResize : function(w, h){
8644 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8645 //        if(typeof w == 'number'){
8646 //            var x = w - this.trigger.getWidth();
8647 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8648 //            this.trigger.setStyle('left', x+'px');
8649 //        }
8650     },
8651
8652     // private
8653     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8654
8655     // private
8656     getResizeEl : function(){
8657         return this.inputEl();
8658     },
8659
8660     // private
8661     getPositionEl : function(){
8662         return this.inputEl();
8663     },
8664
8665     // private
8666     alignErrorIcon : function(){
8667         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8668     },
8669
8670     // private
8671     initEvents : function(){
8672         
8673         this.createList();
8674         
8675         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8676         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8677         if(!this.multiple && this.showToggleBtn){
8678             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8679             if(this.hideTrigger){
8680                 this.trigger.setDisplayed(false);
8681             }
8682             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8683         }
8684         
8685         if(this.multiple){
8686             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8687         }
8688         
8689         //this.trigger.addClassOnOver('x-form-trigger-over');
8690         //this.trigger.addClassOnClick('x-form-trigger-click');
8691         
8692         //if(!this.width){
8693         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8694         //}
8695     },
8696     
8697     createList : function()
8698     {
8699         this.list = Roo.get(document.body).createChild({
8700             tag: 'ul',
8701             cls: 'typeahead typeahead-long dropdown-menu',
8702             style: 'display:none'
8703         });
8704         
8705         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8706         
8707     },
8708
8709     // private
8710     initTrigger : function(){
8711        
8712     },
8713
8714     // private
8715     onDestroy : function(){
8716         if(this.trigger){
8717             this.trigger.removeAllListeners();
8718           //  this.trigger.remove();
8719         }
8720         //if(this.wrap){
8721         //    this.wrap.remove();
8722         //}
8723         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8724     },
8725
8726     // private
8727     onFocus : function(){
8728         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8729         /*
8730         if(!this.mimicing){
8731             this.wrap.addClass('x-trigger-wrap-focus');
8732             this.mimicing = true;
8733             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8734             if(this.monitorTab){
8735                 this.el.on("keydown", this.checkTab, this);
8736             }
8737         }
8738         */
8739     },
8740
8741     // private
8742     checkTab : function(e){
8743         if(e.getKey() == e.TAB){
8744             this.triggerBlur();
8745         }
8746     },
8747
8748     // private
8749     onBlur : function(){
8750         // do nothing
8751     },
8752
8753     // private
8754     mimicBlur : function(e, t){
8755         /*
8756         if(!this.wrap.contains(t) && this.validateBlur()){
8757             this.triggerBlur();
8758         }
8759         */
8760     },
8761
8762     // private
8763     triggerBlur : function(){
8764         this.mimicing = false;
8765         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8766         if(this.monitorTab){
8767             this.el.un("keydown", this.checkTab, this);
8768         }
8769         //this.wrap.removeClass('x-trigger-wrap-focus');
8770         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8771     },
8772
8773     // private
8774     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8775     validateBlur : function(e, t){
8776         return true;
8777     },
8778
8779     // private
8780     onDisable : function(){
8781         this.inputEl().dom.disabled = true;
8782         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8783         //if(this.wrap){
8784         //    this.wrap.addClass('x-item-disabled');
8785         //}
8786     },
8787
8788     // private
8789     onEnable : function(){
8790         this.inputEl().dom.disabled = false;
8791         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8792         //if(this.wrap){
8793         //    this.el.removeClass('x-item-disabled');
8794         //}
8795     },
8796
8797     // private
8798     onShow : function(){
8799         var ae = this.getActionEl();
8800         
8801         if(ae){
8802             ae.dom.style.display = '';
8803             ae.dom.style.visibility = 'visible';
8804         }
8805     },
8806
8807     // private
8808     
8809     onHide : function(){
8810         var ae = this.getActionEl();
8811         ae.dom.style.display = 'none';
8812     },
8813
8814     /**
8815      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8816      * by an implementing function.
8817      * @method
8818      * @param {EventObject} e
8819      */
8820     onTriggerClick : Roo.emptyFn
8821 });
8822  /*
8823  * Based on:
8824  * Ext JS Library 1.1.1
8825  * Copyright(c) 2006-2007, Ext JS, LLC.
8826  *
8827  * Originally Released Under LGPL - original licence link has changed is not relivant.
8828  *
8829  * Fork - LGPL
8830  * <script type="text/javascript">
8831  */
8832
8833
8834 /**
8835  * @class Roo.data.SortTypes
8836  * @singleton
8837  * Defines the default sorting (casting?) comparison functions used when sorting data.
8838  */
8839 Roo.data.SortTypes = {
8840     /**
8841      * Default sort that does nothing
8842      * @param {Mixed} s The value being converted
8843      * @return {Mixed} The comparison value
8844      */
8845     none : function(s){
8846         return s;
8847     },
8848     
8849     /**
8850      * The regular expression used to strip tags
8851      * @type {RegExp}
8852      * @property
8853      */
8854     stripTagsRE : /<\/?[^>]+>/gi,
8855     
8856     /**
8857      * Strips all HTML tags to sort on text only
8858      * @param {Mixed} s The value being converted
8859      * @return {String} The comparison value
8860      */
8861     asText : function(s){
8862         return String(s).replace(this.stripTagsRE, "");
8863     },
8864     
8865     /**
8866      * Strips all HTML tags to sort on text only - Case insensitive
8867      * @param {Mixed} s The value being converted
8868      * @return {String} The comparison value
8869      */
8870     asUCText : function(s){
8871         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8872     },
8873     
8874     /**
8875      * Case insensitive string
8876      * @param {Mixed} s The value being converted
8877      * @return {String} The comparison value
8878      */
8879     asUCString : function(s) {
8880         return String(s).toUpperCase();
8881     },
8882     
8883     /**
8884      * Date sorting
8885      * @param {Mixed} s The value being converted
8886      * @return {Number} The comparison value
8887      */
8888     asDate : function(s) {
8889         if(!s){
8890             return 0;
8891         }
8892         if(s instanceof Date){
8893             return s.getTime();
8894         }
8895         return Date.parse(String(s));
8896     },
8897     
8898     /**
8899      * Float sorting
8900      * @param {Mixed} s The value being converted
8901      * @return {Float} The comparison value
8902      */
8903     asFloat : function(s) {
8904         var val = parseFloat(String(s).replace(/,/g, ""));
8905         if(isNaN(val)) val = 0;
8906         return val;
8907     },
8908     
8909     /**
8910      * Integer sorting
8911      * @param {Mixed} s The value being converted
8912      * @return {Number} The comparison value
8913      */
8914     asInt : function(s) {
8915         var val = parseInt(String(s).replace(/,/g, ""));
8916         if(isNaN(val)) val = 0;
8917         return val;
8918     }
8919 };/*
8920  * Based on:
8921  * Ext JS Library 1.1.1
8922  * Copyright(c) 2006-2007, Ext JS, LLC.
8923  *
8924  * Originally Released Under LGPL - original licence link has changed is not relivant.
8925  *
8926  * Fork - LGPL
8927  * <script type="text/javascript">
8928  */
8929
8930 /**
8931 * @class Roo.data.Record
8932  * Instances of this class encapsulate both record <em>definition</em> information, and record
8933  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8934  * to access Records cached in an {@link Roo.data.Store} object.<br>
8935  * <p>
8936  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8937  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8938  * objects.<br>
8939  * <p>
8940  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8941  * @constructor
8942  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8943  * {@link #create}. The parameters are the same.
8944  * @param {Array} data An associative Array of data values keyed by the field name.
8945  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8946  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8947  * not specified an integer id is generated.
8948  */
8949 Roo.data.Record = function(data, id){
8950     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8951     this.data = data;
8952 };
8953
8954 /**
8955  * Generate a constructor for a specific record layout.
8956  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8957  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8958  * Each field definition object may contain the following properties: <ul>
8959  * <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,
8960  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8961  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8962  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8963  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8964  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8965  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8966  * this may be omitted.</p></li>
8967  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8968  * <ul><li>auto (Default, implies no conversion)</li>
8969  * <li>string</li>
8970  * <li>int</li>
8971  * <li>float</li>
8972  * <li>boolean</li>
8973  * <li>date</li></ul></p></li>
8974  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8975  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8976  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8977  * by the Reader into an object that will be stored in the Record. It is passed the
8978  * following parameters:<ul>
8979  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8980  * </ul></p></li>
8981  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8982  * </ul>
8983  * <br>usage:<br><pre><code>
8984 var TopicRecord = Roo.data.Record.create(
8985     {name: 'title', mapping: 'topic_title'},
8986     {name: 'author', mapping: 'username'},
8987     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8988     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8989     {name: 'lastPoster', mapping: 'user2'},
8990     {name: 'excerpt', mapping: 'post_text'}
8991 );
8992
8993 var myNewRecord = new TopicRecord({
8994     title: 'Do my job please',
8995     author: 'noobie',
8996     totalPosts: 1,
8997     lastPost: new Date(),
8998     lastPoster: 'Animal',
8999     excerpt: 'No way dude!'
9000 });
9001 myStore.add(myNewRecord);
9002 </code></pre>
9003  * @method create
9004  * @static
9005  */
9006 Roo.data.Record.create = function(o){
9007     var f = function(){
9008         f.superclass.constructor.apply(this, arguments);
9009     };
9010     Roo.extend(f, Roo.data.Record);
9011     var p = f.prototype;
9012     p.fields = new Roo.util.MixedCollection(false, function(field){
9013         return field.name;
9014     });
9015     for(var i = 0, len = o.length; i < len; i++){
9016         p.fields.add(new Roo.data.Field(o[i]));
9017     }
9018     f.getField = function(name){
9019         return p.fields.get(name);  
9020     };
9021     return f;
9022 };
9023
9024 Roo.data.Record.AUTO_ID = 1000;
9025 Roo.data.Record.EDIT = 'edit';
9026 Roo.data.Record.REJECT = 'reject';
9027 Roo.data.Record.COMMIT = 'commit';
9028
9029 Roo.data.Record.prototype = {
9030     /**
9031      * Readonly flag - true if this record has been modified.
9032      * @type Boolean
9033      */
9034     dirty : false,
9035     editing : false,
9036     error: null,
9037     modified: null,
9038
9039     // private
9040     join : function(store){
9041         this.store = store;
9042     },
9043
9044     /**
9045      * Set the named field to the specified value.
9046      * @param {String} name The name of the field to set.
9047      * @param {Object} value The value to set the field to.
9048      */
9049     set : function(name, value){
9050         if(this.data[name] == value){
9051             return;
9052         }
9053         this.dirty = true;
9054         if(!this.modified){
9055             this.modified = {};
9056         }
9057         if(typeof this.modified[name] == 'undefined'){
9058             this.modified[name] = this.data[name];
9059         }
9060         this.data[name] = value;
9061         if(!this.editing && this.store){
9062             this.store.afterEdit(this);
9063         }       
9064     },
9065
9066     /**
9067      * Get the value of the named field.
9068      * @param {String} name The name of the field to get the value of.
9069      * @return {Object} The value of the field.
9070      */
9071     get : function(name){
9072         return this.data[name]; 
9073     },
9074
9075     // private
9076     beginEdit : function(){
9077         this.editing = true;
9078         this.modified = {}; 
9079     },
9080
9081     // private
9082     cancelEdit : function(){
9083         this.editing = false;
9084         delete this.modified;
9085     },
9086
9087     // private
9088     endEdit : function(){
9089         this.editing = false;
9090         if(this.dirty && this.store){
9091             this.store.afterEdit(this);
9092         }
9093     },
9094
9095     /**
9096      * Usually called by the {@link Roo.data.Store} which owns the Record.
9097      * Rejects all changes made to the Record since either creation, or the last commit operation.
9098      * Modified fields are reverted to their original values.
9099      * <p>
9100      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9101      * of reject operations.
9102      */
9103     reject : function(){
9104         var m = this.modified;
9105         for(var n in m){
9106             if(typeof m[n] != "function"){
9107                 this.data[n] = m[n];
9108             }
9109         }
9110         this.dirty = false;
9111         delete this.modified;
9112         this.editing = false;
9113         if(this.store){
9114             this.store.afterReject(this);
9115         }
9116     },
9117
9118     /**
9119      * Usually called by the {@link Roo.data.Store} which owns the Record.
9120      * Commits all changes made to the Record since either creation, or the last commit operation.
9121      * <p>
9122      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9123      * of commit operations.
9124      */
9125     commit : function(){
9126         this.dirty = false;
9127         delete this.modified;
9128         this.editing = false;
9129         if(this.store){
9130             this.store.afterCommit(this);
9131         }
9132     },
9133
9134     // private
9135     hasError : function(){
9136         return this.error != null;
9137     },
9138
9139     // private
9140     clearError : function(){
9141         this.error = null;
9142     },
9143
9144     /**
9145      * Creates a copy of this record.
9146      * @param {String} id (optional) A new record id if you don't want to use this record's id
9147      * @return {Record}
9148      */
9149     copy : function(newId) {
9150         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9151     }
9152 };/*
9153  * Based on:
9154  * Ext JS Library 1.1.1
9155  * Copyright(c) 2006-2007, Ext JS, LLC.
9156  *
9157  * Originally Released Under LGPL - original licence link has changed is not relivant.
9158  *
9159  * Fork - LGPL
9160  * <script type="text/javascript">
9161  */
9162
9163
9164
9165 /**
9166  * @class Roo.data.Store
9167  * @extends Roo.util.Observable
9168  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9169  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9170  * <p>
9171  * 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
9172  * has no knowledge of the format of the data returned by the Proxy.<br>
9173  * <p>
9174  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9175  * instances from the data object. These records are cached and made available through accessor functions.
9176  * @constructor
9177  * Creates a new Store.
9178  * @param {Object} config A config object containing the objects needed for the Store to access data,
9179  * and read the data into Records.
9180  */
9181 Roo.data.Store = function(config){
9182     this.data = new Roo.util.MixedCollection(false);
9183     this.data.getKey = function(o){
9184         return o.id;
9185     };
9186     this.baseParams = {};
9187     // private
9188     this.paramNames = {
9189         "start" : "start",
9190         "limit" : "limit",
9191         "sort" : "sort",
9192         "dir" : "dir",
9193         "multisort" : "_multisort"
9194     };
9195
9196     if(config && config.data){
9197         this.inlineData = config.data;
9198         delete config.data;
9199     }
9200
9201     Roo.apply(this, config);
9202     
9203     if(this.reader){ // reader passed
9204         this.reader = Roo.factory(this.reader, Roo.data);
9205         this.reader.xmodule = this.xmodule || false;
9206         if(!this.recordType){
9207             this.recordType = this.reader.recordType;
9208         }
9209         if(this.reader.onMetaChange){
9210             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9211         }
9212     }
9213
9214     if(this.recordType){
9215         this.fields = this.recordType.prototype.fields;
9216     }
9217     this.modified = [];
9218
9219     this.addEvents({
9220         /**
9221          * @event datachanged
9222          * Fires when the data cache has changed, and a widget which is using this Store
9223          * as a Record cache should refresh its view.
9224          * @param {Store} this
9225          */
9226         datachanged : true,
9227         /**
9228          * @event metachange
9229          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9230          * @param {Store} this
9231          * @param {Object} meta The JSON metadata
9232          */
9233         metachange : true,
9234         /**
9235          * @event add
9236          * Fires when Records have been added to the Store
9237          * @param {Store} this
9238          * @param {Roo.data.Record[]} records The array of Records added
9239          * @param {Number} index The index at which the record(s) were added
9240          */
9241         add : true,
9242         /**
9243          * @event remove
9244          * Fires when a Record has been removed from the Store
9245          * @param {Store} this
9246          * @param {Roo.data.Record} record The Record that was removed
9247          * @param {Number} index The index at which the record was removed
9248          */
9249         remove : true,
9250         /**
9251          * @event update
9252          * Fires when a Record has been updated
9253          * @param {Store} this
9254          * @param {Roo.data.Record} record The Record that was updated
9255          * @param {String} operation The update operation being performed.  Value may be one of:
9256          * <pre><code>
9257  Roo.data.Record.EDIT
9258  Roo.data.Record.REJECT
9259  Roo.data.Record.COMMIT
9260          * </code></pre>
9261          */
9262         update : true,
9263         /**
9264          * @event clear
9265          * Fires when the data cache has been cleared.
9266          * @param {Store} this
9267          */
9268         clear : true,
9269         /**
9270          * @event beforeload
9271          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9272          * the load action will be canceled.
9273          * @param {Store} this
9274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9275          */
9276         beforeload : true,
9277         /**
9278          * @event beforeloadadd
9279          * Fires after a new set of Records has been loaded.
9280          * @param {Store} this
9281          * @param {Roo.data.Record[]} records The Records that were loaded
9282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9283          */
9284         beforeloadadd : true,
9285         /**
9286          * @event load
9287          * Fires after a new set of Records has been loaded, before they are added to the store.
9288          * @param {Store} this
9289          * @param {Roo.data.Record[]} records The Records that were loaded
9290          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9291          * @params {Object} return from reader
9292          */
9293         load : true,
9294         /**
9295          * @event loadexception
9296          * Fires if an exception occurs in the Proxy during loading.
9297          * Called with the signature of the Proxy's "loadexception" event.
9298          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9299          * 
9300          * @param {Proxy} 
9301          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9302          * @param {Object} load options 
9303          * @param {Object} jsonData from your request (normally this contains the Exception)
9304          */
9305         loadexception : true
9306     });
9307     
9308     if(this.proxy){
9309         this.proxy = Roo.factory(this.proxy, Roo.data);
9310         this.proxy.xmodule = this.xmodule || false;
9311         this.relayEvents(this.proxy,  ["loadexception"]);
9312     }
9313     this.sortToggle = {};
9314     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9315
9316     Roo.data.Store.superclass.constructor.call(this);
9317
9318     if(this.inlineData){
9319         this.loadData(this.inlineData);
9320         delete this.inlineData;
9321     }
9322 };
9323
9324 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9325      /**
9326     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9327     * without a remote query - used by combo/forms at present.
9328     */
9329     
9330     /**
9331     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9332     */
9333     /**
9334     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9335     */
9336     /**
9337     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9338     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9339     */
9340     /**
9341     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9342     * on any HTTP request
9343     */
9344     /**
9345     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9346     */
9347     /**
9348     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9349     */
9350     multiSort: false,
9351     /**
9352     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9353     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9354     */
9355     remoteSort : false,
9356
9357     /**
9358     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9359      * loaded or when a record is removed. (defaults to false).
9360     */
9361     pruneModifiedRecords : false,
9362
9363     // private
9364     lastOptions : null,
9365
9366     /**
9367      * Add Records to the Store and fires the add event.
9368      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9369      */
9370     add : function(records){
9371         records = [].concat(records);
9372         for(var i = 0, len = records.length; i < len; i++){
9373             records[i].join(this);
9374         }
9375         var index = this.data.length;
9376         this.data.addAll(records);
9377         this.fireEvent("add", this, records, index);
9378     },
9379
9380     /**
9381      * Remove a Record from the Store and fires the remove event.
9382      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9383      */
9384     remove : function(record){
9385         var index = this.data.indexOf(record);
9386         this.data.removeAt(index);
9387         if(this.pruneModifiedRecords){
9388             this.modified.remove(record);
9389         }
9390         this.fireEvent("remove", this, record, index);
9391     },
9392
9393     /**
9394      * Remove all Records from the Store and fires the clear event.
9395      */
9396     removeAll : function(){
9397         this.data.clear();
9398         if(this.pruneModifiedRecords){
9399             this.modified = [];
9400         }
9401         this.fireEvent("clear", this);
9402     },
9403
9404     /**
9405      * Inserts Records to the Store at the given index and fires the add event.
9406      * @param {Number} index The start index at which to insert the passed Records.
9407      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9408      */
9409     insert : function(index, records){
9410         records = [].concat(records);
9411         for(var i = 0, len = records.length; i < len; i++){
9412             this.data.insert(index, records[i]);
9413             records[i].join(this);
9414         }
9415         this.fireEvent("add", this, records, index);
9416     },
9417
9418     /**
9419      * Get the index within the cache of the passed Record.
9420      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9421      * @return {Number} The index of the passed Record. Returns -1 if not found.
9422      */
9423     indexOf : function(record){
9424         return this.data.indexOf(record);
9425     },
9426
9427     /**
9428      * Get the index within the cache of the Record with the passed id.
9429      * @param {String} id The id of the Record to find.
9430      * @return {Number} The index of the Record. Returns -1 if not found.
9431      */
9432     indexOfId : function(id){
9433         return this.data.indexOfKey(id);
9434     },
9435
9436     /**
9437      * Get the Record with the specified id.
9438      * @param {String} id The id of the Record to find.
9439      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9440      */
9441     getById : function(id){
9442         return this.data.key(id);
9443     },
9444
9445     /**
9446      * Get the Record at the specified index.
9447      * @param {Number} index The index of the Record to find.
9448      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9449      */
9450     getAt : function(index){
9451         return this.data.itemAt(index);
9452     },
9453
9454     /**
9455      * Returns a range of Records between specified indices.
9456      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9457      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9458      * @return {Roo.data.Record[]} An array of Records
9459      */
9460     getRange : function(start, end){
9461         return this.data.getRange(start, end);
9462     },
9463
9464     // private
9465     storeOptions : function(o){
9466         o = Roo.apply({}, o);
9467         delete o.callback;
9468         delete o.scope;
9469         this.lastOptions = o;
9470     },
9471
9472     /**
9473      * Loads the Record cache from the configured Proxy using the configured Reader.
9474      * <p>
9475      * If using remote paging, then the first load call must specify the <em>start</em>
9476      * and <em>limit</em> properties in the options.params property to establish the initial
9477      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9478      * <p>
9479      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9480      * and this call will return before the new data has been loaded. Perform any post-processing
9481      * in a callback function, or in a "load" event handler.</strong>
9482      * <p>
9483      * @param {Object} options An object containing properties which control loading options:<ul>
9484      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9485      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9486      * passed the following arguments:<ul>
9487      * <li>r : Roo.data.Record[]</li>
9488      * <li>options: Options object from the load call</li>
9489      * <li>success: Boolean success indicator</li></ul></li>
9490      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9491      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9492      * </ul>
9493      */
9494     load : function(options){
9495         options = options || {};
9496         if(this.fireEvent("beforeload", this, options) !== false){
9497             this.storeOptions(options);
9498             var p = Roo.apply(options.params || {}, this.baseParams);
9499             // if meta was not loaded from remote source.. try requesting it.
9500             if (!this.reader.metaFromRemote) {
9501                 p._requestMeta = 1;
9502             }
9503             if(this.sortInfo && this.remoteSort){
9504                 var pn = this.paramNames;
9505                 p[pn["sort"]] = this.sortInfo.field;
9506                 p[pn["dir"]] = this.sortInfo.direction;
9507             }
9508             if (this.multiSort) {
9509                 var pn = this.paramNames;
9510                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9511             }
9512             
9513             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9514         }
9515     },
9516
9517     /**
9518      * Reloads the Record cache from the configured Proxy using the configured Reader and
9519      * the options from the last load operation performed.
9520      * @param {Object} options (optional) An object containing properties which may override the options
9521      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9522      * the most recently used options are reused).
9523      */
9524     reload : function(options){
9525         this.load(Roo.applyIf(options||{}, this.lastOptions));
9526     },
9527
9528     // private
9529     // Called as a callback by the Reader during a load operation.
9530     loadRecords : function(o, options, success){
9531         if(!o || success === false){
9532             if(success !== false){
9533                 this.fireEvent("load", this, [], options, o);
9534             }
9535             if(options.callback){
9536                 options.callback.call(options.scope || this, [], options, false);
9537             }
9538             return;
9539         }
9540         // if data returned failure - throw an exception.
9541         if (o.success === false) {
9542             // show a message if no listener is registered.
9543             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9544                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9545             }
9546             // loadmask wil be hooked into this..
9547             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9548             return;
9549         }
9550         var r = o.records, t = o.totalRecords || r.length;
9551         
9552         this.fireEvent("beforeloadadd", this, r, options, o);
9553         
9554         if(!options || options.add !== true){
9555             if(this.pruneModifiedRecords){
9556                 this.modified = [];
9557             }
9558             for(var i = 0, len = r.length; i < len; i++){
9559                 r[i].join(this);
9560             }
9561             if(this.snapshot){
9562                 this.data = this.snapshot;
9563                 delete this.snapshot;
9564             }
9565             this.data.clear();
9566             this.data.addAll(r);
9567             this.totalLength = t;
9568             this.applySort();
9569             this.fireEvent("datachanged", this);
9570         }else{
9571             this.totalLength = Math.max(t, this.data.length+r.length);
9572             this.add(r);
9573         }
9574         this.fireEvent("load", this, r, options, o);
9575         if(options.callback){
9576             options.callback.call(options.scope || this, r, options, true);
9577         }
9578     },
9579
9580
9581     /**
9582      * Loads data from a passed data block. A Reader which understands the format of the data
9583      * must have been configured in the constructor.
9584      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9585      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9586      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9587      */
9588     loadData : function(o, append){
9589         var r = this.reader.readRecords(o);
9590         this.loadRecords(r, {add: append}, true);
9591     },
9592
9593     /**
9594      * Gets the number of cached records.
9595      * <p>
9596      * <em>If using paging, this may not be the total size of the dataset. If the data object
9597      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9598      * the data set size</em>
9599      */
9600     getCount : function(){
9601         return this.data.length || 0;
9602     },
9603
9604     /**
9605      * Gets the total number of records in the dataset as returned by the server.
9606      * <p>
9607      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9608      * the dataset size</em>
9609      */
9610     getTotalCount : function(){
9611         return this.totalLength || 0;
9612     },
9613
9614     /**
9615      * Returns the sort state of the Store as an object with two properties:
9616      * <pre><code>
9617  field {String} The name of the field by which the Records are sorted
9618  direction {String} The sort order, "ASC" or "DESC"
9619      * </code></pre>
9620      */
9621     getSortState : function(){
9622         return this.sortInfo;
9623     },
9624
9625     // private
9626     applySort : function(){
9627         if(this.sortInfo && !this.remoteSort){
9628             var s = this.sortInfo, f = s.field;
9629             var st = this.fields.get(f).sortType;
9630             var fn = function(r1, r2){
9631                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9632                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9633             };
9634             this.data.sort(s.direction, fn);
9635             if(this.snapshot && this.snapshot != this.data){
9636                 this.snapshot.sort(s.direction, fn);
9637             }
9638         }
9639     },
9640
9641     /**
9642      * Sets the default sort column and order to be used by the next load operation.
9643      * @param {String} fieldName The name of the field to sort by.
9644      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9645      */
9646     setDefaultSort : function(field, dir){
9647         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9648     },
9649
9650     /**
9651      * Sort the Records.
9652      * If remote sorting is used, the sort is performed on the server, and the cache is
9653      * reloaded. If local sorting is used, the cache is sorted internally.
9654      * @param {String} fieldName The name of the field to sort by.
9655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9656      */
9657     sort : function(fieldName, dir){
9658         var f = this.fields.get(fieldName);
9659         if(!dir){
9660             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9661             
9662             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9663                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9664             }else{
9665                 dir = f.sortDir;
9666             }
9667         }
9668         this.sortToggle[f.name] = dir;
9669         this.sortInfo = {field: f.name, direction: dir};
9670         if(!this.remoteSort){
9671             this.applySort();
9672             this.fireEvent("datachanged", this);
9673         }else{
9674             this.load(this.lastOptions);
9675         }
9676     },
9677
9678     /**
9679      * Calls the specified function for each of the Records in the cache.
9680      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9681      * Returning <em>false</em> aborts and exits the iteration.
9682      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9683      */
9684     each : function(fn, scope){
9685         this.data.each(fn, scope);
9686     },
9687
9688     /**
9689      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9690      * (e.g., during paging).
9691      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9692      */
9693     getModifiedRecords : function(){
9694         return this.modified;
9695     },
9696
9697     // private
9698     createFilterFn : function(property, value, anyMatch){
9699         if(!value.exec){ // not a regex
9700             value = String(value);
9701             if(value.length == 0){
9702                 return false;
9703             }
9704             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9705         }
9706         return function(r){
9707             return value.test(r.data[property]);
9708         };
9709     },
9710
9711     /**
9712      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9713      * @param {String} property A field on your records
9714      * @param {Number} start The record index to start at (defaults to 0)
9715      * @param {Number} end The last record index to include (defaults to length - 1)
9716      * @return {Number} The sum
9717      */
9718     sum : function(property, start, end){
9719         var rs = this.data.items, v = 0;
9720         start = start || 0;
9721         end = (end || end === 0) ? end : rs.length-1;
9722
9723         for(var i = start; i <= end; i++){
9724             v += (rs[i].data[property] || 0);
9725         }
9726         return v;
9727     },
9728
9729     /**
9730      * Filter the records by a specified property.
9731      * @param {String} field A field on your records
9732      * @param {String/RegExp} value Either a string that the field
9733      * should start with or a RegExp to test against the field
9734      * @param {Boolean} anyMatch True to match any part not just the beginning
9735      */
9736     filter : function(property, value, anyMatch){
9737         var fn = this.createFilterFn(property, value, anyMatch);
9738         return fn ? this.filterBy(fn) : this.clearFilter();
9739     },
9740
9741     /**
9742      * Filter by a function. The specified function will be called with each
9743      * record in this data source. If the function returns true the record is included,
9744      * otherwise it is filtered.
9745      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9746      * @param {Object} scope (optional) The scope of the function (defaults to this)
9747      */
9748     filterBy : function(fn, scope){
9749         this.snapshot = this.snapshot || this.data;
9750         this.data = this.queryBy(fn, scope||this);
9751         this.fireEvent("datachanged", this);
9752     },
9753
9754     /**
9755      * Query the records by a specified property.
9756      * @param {String} field A field on your records
9757      * @param {String/RegExp} value Either a string that the field
9758      * should start with or a RegExp to test against the field
9759      * @param {Boolean} anyMatch True to match any part not just the beginning
9760      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9761      */
9762     query : function(property, value, anyMatch){
9763         var fn = this.createFilterFn(property, value, anyMatch);
9764         return fn ? this.queryBy(fn) : this.data.clone();
9765     },
9766
9767     /**
9768      * Query by a function. The specified function will be called with each
9769      * record in this data source. If the function returns true the record is included
9770      * in the results.
9771      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9772      * @param {Object} scope (optional) The scope of the function (defaults to this)
9773       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9774      **/
9775     queryBy : function(fn, scope){
9776         var data = this.snapshot || this.data;
9777         return data.filterBy(fn, scope||this);
9778     },
9779
9780     /**
9781      * Collects unique values for a particular dataIndex from this store.
9782      * @param {String} dataIndex The property to collect
9783      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9784      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9785      * @return {Array} An array of the unique values
9786      **/
9787     collect : function(dataIndex, allowNull, bypassFilter){
9788         var d = (bypassFilter === true && this.snapshot) ?
9789                 this.snapshot.items : this.data.items;
9790         var v, sv, r = [], l = {};
9791         for(var i = 0, len = d.length; i < len; i++){
9792             v = d[i].data[dataIndex];
9793             sv = String(v);
9794             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9795                 l[sv] = true;
9796                 r[r.length] = v;
9797             }
9798         }
9799         return r;
9800     },
9801
9802     /**
9803      * Revert to a view of the Record cache with no filtering applied.
9804      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9805      */
9806     clearFilter : function(suppressEvent){
9807         if(this.snapshot && this.snapshot != this.data){
9808             this.data = this.snapshot;
9809             delete this.snapshot;
9810             if(suppressEvent !== true){
9811                 this.fireEvent("datachanged", this);
9812             }
9813         }
9814     },
9815
9816     // private
9817     afterEdit : function(record){
9818         if(this.modified.indexOf(record) == -1){
9819             this.modified.push(record);
9820         }
9821         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9822     },
9823     
9824     // private
9825     afterReject : function(record){
9826         this.modified.remove(record);
9827         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9828     },
9829
9830     // private
9831     afterCommit : function(record){
9832         this.modified.remove(record);
9833         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9834     },
9835
9836     /**
9837      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9838      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9839      */
9840     commitChanges : function(){
9841         var m = this.modified.slice(0);
9842         this.modified = [];
9843         for(var i = 0, len = m.length; i < len; i++){
9844             m[i].commit();
9845         }
9846     },
9847
9848     /**
9849      * Cancel outstanding changes on all changed records.
9850      */
9851     rejectChanges : function(){
9852         var m = this.modified.slice(0);
9853         this.modified = [];
9854         for(var i = 0, len = m.length; i < len; i++){
9855             m[i].reject();
9856         }
9857     },
9858
9859     onMetaChange : function(meta, rtype, o){
9860         this.recordType = rtype;
9861         this.fields = rtype.prototype.fields;
9862         delete this.snapshot;
9863         this.sortInfo = meta.sortInfo || this.sortInfo;
9864         this.modified = [];
9865         this.fireEvent('metachange', this, this.reader.meta);
9866     },
9867     
9868     moveIndex : function(data, type)
9869     {
9870         var index = this.indexOf(data);
9871         
9872         var newIndex = index + type;
9873         
9874         this.remove(data);
9875         
9876         this.insert(newIndex, data);
9877         
9878     }
9879 });/*
9880  * Based on:
9881  * Ext JS Library 1.1.1
9882  * Copyright(c) 2006-2007, Ext JS, LLC.
9883  *
9884  * Originally Released Under LGPL - original licence link has changed is not relivant.
9885  *
9886  * Fork - LGPL
9887  * <script type="text/javascript">
9888  */
9889
9890 /**
9891  * @class Roo.data.SimpleStore
9892  * @extends Roo.data.Store
9893  * Small helper class to make creating Stores from Array data easier.
9894  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9895  * @cfg {Array} fields An array of field definition objects, or field name strings.
9896  * @cfg {Array} data The multi-dimensional array of data
9897  * @constructor
9898  * @param {Object} config
9899  */
9900 Roo.data.SimpleStore = function(config){
9901     Roo.data.SimpleStore.superclass.constructor.call(this, {
9902         isLocal : true,
9903         reader: new Roo.data.ArrayReader({
9904                 id: config.id
9905             },
9906             Roo.data.Record.create(config.fields)
9907         ),
9908         proxy : new Roo.data.MemoryProxy(config.data)
9909     });
9910     this.load();
9911 };
9912 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9913  * Based on:
9914  * Ext JS Library 1.1.1
9915  * Copyright(c) 2006-2007, Ext JS, LLC.
9916  *
9917  * Originally Released Under LGPL - original licence link has changed is not relivant.
9918  *
9919  * Fork - LGPL
9920  * <script type="text/javascript">
9921  */
9922
9923 /**
9924 /**
9925  * @extends Roo.data.Store
9926  * @class Roo.data.JsonStore
9927  * Small helper class to make creating Stores for JSON data easier. <br/>
9928 <pre><code>
9929 var store = new Roo.data.JsonStore({
9930     url: 'get-images.php',
9931     root: 'images',
9932     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9933 });
9934 </code></pre>
9935  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9936  * JsonReader and HttpProxy (unless inline data is provided).</b>
9937  * @cfg {Array} fields An array of field definition objects, or field name strings.
9938  * @constructor
9939  * @param {Object} config
9940  */
9941 Roo.data.JsonStore = function(c){
9942     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9943         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9944         reader: new Roo.data.JsonReader(c, c.fields)
9945     }));
9946 };
9947 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958  
9959 Roo.data.Field = function(config){
9960     if(typeof config == "string"){
9961         config = {name: config};
9962     }
9963     Roo.apply(this, config);
9964     
9965     if(!this.type){
9966         this.type = "auto";
9967     }
9968     
9969     var st = Roo.data.SortTypes;
9970     // named sortTypes are supported, here we look them up
9971     if(typeof this.sortType == "string"){
9972         this.sortType = st[this.sortType];
9973     }
9974     
9975     // set default sortType for strings and dates
9976     if(!this.sortType){
9977         switch(this.type){
9978             case "string":
9979                 this.sortType = st.asUCString;
9980                 break;
9981             case "date":
9982                 this.sortType = st.asDate;
9983                 break;
9984             default:
9985                 this.sortType = st.none;
9986         }
9987     }
9988
9989     // define once
9990     var stripRe = /[\$,%]/g;
9991
9992     // prebuilt conversion function for this field, instead of
9993     // switching every time we're reading a value
9994     if(!this.convert){
9995         var cv, dateFormat = this.dateFormat;
9996         switch(this.type){
9997             case "":
9998             case "auto":
9999             case undefined:
10000                 cv = function(v){ return v; };
10001                 break;
10002             case "string":
10003                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10004                 break;
10005             case "int":
10006                 cv = function(v){
10007                     return v !== undefined && v !== null && v !== '' ?
10008                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10009                     };
10010                 break;
10011             case "float":
10012                 cv = function(v){
10013                     return v !== undefined && v !== null && v !== '' ?
10014                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10015                     };
10016                 break;
10017             case "bool":
10018             case "boolean":
10019                 cv = function(v){ return v === true || v === "true" || v == 1; };
10020                 break;
10021             case "date":
10022                 cv = function(v){
10023                     if(!v){
10024                         return '';
10025                     }
10026                     if(v instanceof Date){
10027                         return v;
10028                     }
10029                     if(dateFormat){
10030                         if(dateFormat == "timestamp"){
10031                             return new Date(v*1000);
10032                         }
10033                         return Date.parseDate(v, dateFormat);
10034                     }
10035                     var parsed = Date.parse(v);
10036                     return parsed ? new Date(parsed) : null;
10037                 };
10038              break;
10039             
10040         }
10041         this.convert = cv;
10042     }
10043 };
10044
10045 Roo.data.Field.prototype = {
10046     dateFormat: null,
10047     defaultValue: "",
10048     mapping: null,
10049     sortType : null,
10050     sortDir : "ASC"
10051 };/*
10052  * Based on:
10053  * Ext JS Library 1.1.1
10054  * Copyright(c) 2006-2007, Ext JS, LLC.
10055  *
10056  * Originally Released Under LGPL - original licence link has changed is not relivant.
10057  *
10058  * Fork - LGPL
10059  * <script type="text/javascript">
10060  */
10061  
10062 // Base class for reading structured data from a data source.  This class is intended to be
10063 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10064
10065 /**
10066  * @class Roo.data.DataReader
10067  * Base class for reading structured data from a data source.  This class is intended to be
10068  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10069  */
10070
10071 Roo.data.DataReader = function(meta, recordType){
10072     
10073     this.meta = meta;
10074     
10075     this.recordType = recordType instanceof Array ? 
10076         Roo.data.Record.create(recordType) : recordType;
10077 };
10078
10079 Roo.data.DataReader.prototype = {
10080      /**
10081      * Create an empty record
10082      * @param {Object} data (optional) - overlay some values
10083      * @return {Roo.data.Record} record created.
10084      */
10085     newRow :  function(d) {
10086         var da =  {};
10087         this.recordType.prototype.fields.each(function(c) {
10088             switch( c.type) {
10089                 case 'int' : da[c.name] = 0; break;
10090                 case 'date' : da[c.name] = new Date(); break;
10091                 case 'float' : da[c.name] = 0.0; break;
10092                 case 'boolean' : da[c.name] = false; break;
10093                 default : da[c.name] = ""; break;
10094             }
10095             
10096         });
10097         return new this.recordType(Roo.apply(da, d));
10098     }
10099     
10100 };/*
10101  * Based on:
10102  * Ext JS Library 1.1.1
10103  * Copyright(c) 2006-2007, Ext JS, LLC.
10104  *
10105  * Originally Released Under LGPL - original licence link has changed is not relivant.
10106  *
10107  * Fork - LGPL
10108  * <script type="text/javascript">
10109  */
10110
10111 /**
10112  * @class Roo.data.DataProxy
10113  * @extends Roo.data.Observable
10114  * This class is an abstract base class for implementations which provide retrieval of
10115  * unformatted data objects.<br>
10116  * <p>
10117  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10118  * (of the appropriate type which knows how to parse the data object) to provide a block of
10119  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10120  * <p>
10121  * Custom implementations must implement the load method as described in
10122  * {@link Roo.data.HttpProxy#load}.
10123  */
10124 Roo.data.DataProxy = function(){
10125     this.addEvents({
10126         /**
10127          * @event beforeload
10128          * Fires before a network request is made to retrieve a data object.
10129          * @param {Object} This DataProxy object.
10130          * @param {Object} params The params parameter to the load function.
10131          */
10132         beforeload : true,
10133         /**
10134          * @event load
10135          * Fires before the load method's callback is called.
10136          * @param {Object} This DataProxy object.
10137          * @param {Object} o The data object.
10138          * @param {Object} arg The callback argument object passed to the load function.
10139          */
10140         load : true,
10141         /**
10142          * @event loadexception
10143          * Fires if an Exception occurs during data retrieval.
10144          * @param {Object} This DataProxy object.
10145          * @param {Object} o The data object.
10146          * @param {Object} arg The callback argument object passed to the load function.
10147          * @param {Object} e The Exception.
10148          */
10149         loadexception : true
10150     });
10151     Roo.data.DataProxy.superclass.constructor.call(this);
10152 };
10153
10154 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10155
10156     /**
10157      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10158      */
10159 /*
10160  * Based on:
10161  * Ext JS Library 1.1.1
10162  * Copyright(c) 2006-2007, Ext JS, LLC.
10163  *
10164  * Originally Released Under LGPL - original licence link has changed is not relivant.
10165  *
10166  * Fork - LGPL
10167  * <script type="text/javascript">
10168  */
10169 /**
10170  * @class Roo.data.MemoryProxy
10171  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10172  * to the Reader when its load method is called.
10173  * @constructor
10174  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10175  */
10176 Roo.data.MemoryProxy = function(data){
10177     if (data.data) {
10178         data = data.data;
10179     }
10180     Roo.data.MemoryProxy.superclass.constructor.call(this);
10181     this.data = data;
10182 };
10183
10184 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10185     /**
10186      * Load data from the requested source (in this case an in-memory
10187      * data object passed to the constructor), read the data object into
10188      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10189      * process that block using the passed callback.
10190      * @param {Object} params This parameter is not used by the MemoryProxy class.
10191      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10192      * object into a block of Roo.data.Records.
10193      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10194      * The function must be passed <ul>
10195      * <li>The Record block object</li>
10196      * <li>The "arg" argument from the load function</li>
10197      * <li>A boolean success indicator</li>
10198      * </ul>
10199      * @param {Object} scope The scope in which to call the callback
10200      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10201      */
10202     load : function(params, reader, callback, scope, arg){
10203         params = params || {};
10204         var result;
10205         try {
10206             result = reader.readRecords(this.data);
10207         }catch(e){
10208             this.fireEvent("loadexception", this, arg, null, e);
10209             callback.call(scope, null, arg, false);
10210             return;
10211         }
10212         callback.call(scope, result, arg, true);
10213     },
10214     
10215     // private
10216     update : function(params, records){
10217         
10218     }
10219 });/*
10220  * Based on:
10221  * Ext JS Library 1.1.1
10222  * Copyright(c) 2006-2007, Ext JS, LLC.
10223  *
10224  * Originally Released Under LGPL - original licence link has changed is not relivant.
10225  *
10226  * Fork - LGPL
10227  * <script type="text/javascript">
10228  */
10229 /**
10230  * @class Roo.data.HttpProxy
10231  * @extends Roo.data.DataProxy
10232  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10233  * configured to reference a certain URL.<br><br>
10234  * <p>
10235  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10236  * from which the running page was served.<br><br>
10237  * <p>
10238  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10239  * <p>
10240  * Be aware that to enable the browser to parse an XML document, the server must set
10241  * the Content-Type header in the HTTP response to "text/xml".
10242  * @constructor
10243  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10244  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10245  * will be used to make the request.
10246  */
10247 Roo.data.HttpProxy = function(conn){
10248     Roo.data.HttpProxy.superclass.constructor.call(this);
10249     // is conn a conn config or a real conn?
10250     this.conn = conn;
10251     this.useAjax = !conn || !conn.events;
10252   
10253 };
10254
10255 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10256     // thse are take from connection...
10257     
10258     /**
10259      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10260      */
10261     /**
10262      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10263      * extra parameters to each request made by this object. (defaults to undefined)
10264      */
10265     /**
10266      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10267      *  to each request made by this object. (defaults to undefined)
10268      */
10269     /**
10270      * @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)
10271      */
10272     /**
10273      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10274      */
10275      /**
10276      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10277      * @type Boolean
10278      */
10279   
10280
10281     /**
10282      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10283      * @type Boolean
10284      */
10285     /**
10286      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10287      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10288      * a finer-grained basis than the DataProxy events.
10289      */
10290     getConnection : function(){
10291         return this.useAjax ? Roo.Ajax : this.conn;
10292     },
10293
10294     /**
10295      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10296      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10297      * process that block using the passed callback.
10298      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10299      * for the request to the remote server.
10300      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10301      * object into a block of Roo.data.Records.
10302      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10303      * The function must be passed <ul>
10304      * <li>The Record block object</li>
10305      * <li>The "arg" argument from the load function</li>
10306      * <li>A boolean success indicator</li>
10307      * </ul>
10308      * @param {Object} scope The scope in which to call the callback
10309      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10310      */
10311     load : function(params, reader, callback, scope, arg){
10312         if(this.fireEvent("beforeload", this, params) !== false){
10313             var  o = {
10314                 params : params || {},
10315                 request: {
10316                     callback : callback,
10317                     scope : scope,
10318                     arg : arg
10319                 },
10320                 reader: reader,
10321                 callback : this.loadResponse,
10322                 scope: this
10323             };
10324             if(this.useAjax){
10325                 Roo.applyIf(o, this.conn);
10326                 if(this.activeRequest){
10327                     Roo.Ajax.abort(this.activeRequest);
10328                 }
10329                 this.activeRequest = Roo.Ajax.request(o);
10330             }else{
10331                 this.conn.request(o);
10332             }
10333         }else{
10334             callback.call(scope||this, null, arg, false);
10335         }
10336     },
10337
10338     // private
10339     loadResponse : function(o, success, response){
10340         delete this.activeRequest;
10341         if(!success){
10342             this.fireEvent("loadexception", this, o, response);
10343             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10344             return;
10345         }
10346         var result;
10347         try {
10348             result = o.reader.read(response);
10349         }catch(e){
10350             this.fireEvent("loadexception", this, o, response, e);
10351             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10352             return;
10353         }
10354         
10355         this.fireEvent("load", this, o, o.request.arg);
10356         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10357     },
10358
10359     // private
10360     update : function(dataSet){
10361
10362     },
10363
10364     // private
10365     updateResponse : function(dataSet){
10366
10367     }
10368 });/*
10369  * Based on:
10370  * Ext JS Library 1.1.1
10371  * Copyright(c) 2006-2007, Ext JS, LLC.
10372  *
10373  * Originally Released Under LGPL - original licence link has changed is not relivant.
10374  *
10375  * Fork - LGPL
10376  * <script type="text/javascript">
10377  */
10378
10379 /**
10380  * @class Roo.data.ScriptTagProxy
10381  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10382  * other than the originating domain of the running page.<br><br>
10383  * <p>
10384  * <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
10385  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10386  * <p>
10387  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10388  * source code that is used as the source inside a &lt;script> tag.<br><br>
10389  * <p>
10390  * In order for the browser to process the returned data, the server must wrap the data object
10391  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10392  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10393  * depending on whether the callback name was passed:
10394  * <p>
10395  * <pre><code>
10396 boolean scriptTag = false;
10397 String cb = request.getParameter("callback");
10398 if (cb != null) {
10399     scriptTag = true;
10400     response.setContentType("text/javascript");
10401 } else {
10402     response.setContentType("application/x-json");
10403 }
10404 Writer out = response.getWriter();
10405 if (scriptTag) {
10406     out.write(cb + "(");
10407 }
10408 out.print(dataBlock.toJsonString());
10409 if (scriptTag) {
10410     out.write(");");
10411 }
10412 </pre></code>
10413  *
10414  * @constructor
10415  * @param {Object} config A configuration object.
10416  */
10417 Roo.data.ScriptTagProxy = function(config){
10418     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10419     Roo.apply(this, config);
10420     this.head = document.getElementsByTagName("head")[0];
10421 };
10422
10423 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10424
10425 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10426     /**
10427      * @cfg {String} url The URL from which to request the data object.
10428      */
10429     /**
10430      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10431      */
10432     timeout : 30000,
10433     /**
10434      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10435      * the server the name of the callback function set up by the load call to process the returned data object.
10436      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10437      * javascript output which calls this named function passing the data object as its only parameter.
10438      */
10439     callbackParam : "callback",
10440     /**
10441      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10442      * name to the request.
10443      */
10444     nocache : true,
10445
10446     /**
10447      * Load data from the configured URL, read the data object into
10448      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10449      * process that block using the passed callback.
10450      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10451      * for the request to the remote server.
10452      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10453      * object into a block of Roo.data.Records.
10454      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10455      * The function must be passed <ul>
10456      * <li>The Record block object</li>
10457      * <li>The "arg" argument from the load function</li>
10458      * <li>A boolean success indicator</li>
10459      * </ul>
10460      * @param {Object} scope The scope in which to call the callback
10461      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10462      */
10463     load : function(params, reader, callback, scope, arg){
10464         if(this.fireEvent("beforeload", this, params) !== false){
10465
10466             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10467
10468             var url = this.url;
10469             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10470             if(this.nocache){
10471                 url += "&_dc=" + (new Date().getTime());
10472             }
10473             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10474             var trans = {
10475                 id : transId,
10476                 cb : "stcCallback"+transId,
10477                 scriptId : "stcScript"+transId,
10478                 params : params,
10479                 arg : arg,
10480                 url : url,
10481                 callback : callback,
10482                 scope : scope,
10483                 reader : reader
10484             };
10485             var conn = this;
10486
10487             window[trans.cb] = function(o){
10488                 conn.handleResponse(o, trans);
10489             };
10490
10491             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10492
10493             if(this.autoAbort !== false){
10494                 this.abort();
10495             }
10496
10497             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10498
10499             var script = document.createElement("script");
10500             script.setAttribute("src", url);
10501             script.setAttribute("type", "text/javascript");
10502             script.setAttribute("id", trans.scriptId);
10503             this.head.appendChild(script);
10504
10505             this.trans = trans;
10506         }else{
10507             callback.call(scope||this, null, arg, false);
10508         }
10509     },
10510
10511     // private
10512     isLoading : function(){
10513         return this.trans ? true : false;
10514     },
10515
10516     /**
10517      * Abort the current server request.
10518      */
10519     abort : function(){
10520         if(this.isLoading()){
10521             this.destroyTrans(this.trans);
10522         }
10523     },
10524
10525     // private
10526     destroyTrans : function(trans, isLoaded){
10527         this.head.removeChild(document.getElementById(trans.scriptId));
10528         clearTimeout(trans.timeoutId);
10529         if(isLoaded){
10530             window[trans.cb] = undefined;
10531             try{
10532                 delete window[trans.cb];
10533             }catch(e){}
10534         }else{
10535             // if hasn't been loaded, wait for load to remove it to prevent script error
10536             window[trans.cb] = function(){
10537                 window[trans.cb] = undefined;
10538                 try{
10539                     delete window[trans.cb];
10540                 }catch(e){}
10541             };
10542         }
10543     },
10544
10545     // private
10546     handleResponse : function(o, trans){
10547         this.trans = false;
10548         this.destroyTrans(trans, true);
10549         var result;
10550         try {
10551             result = trans.reader.readRecords(o);
10552         }catch(e){
10553             this.fireEvent("loadexception", this, o, trans.arg, e);
10554             trans.callback.call(trans.scope||window, null, trans.arg, false);
10555             return;
10556         }
10557         this.fireEvent("load", this, o, trans.arg);
10558         trans.callback.call(trans.scope||window, result, trans.arg, true);
10559     },
10560
10561     // private
10562     handleFailure : function(trans){
10563         this.trans = false;
10564         this.destroyTrans(trans, false);
10565         this.fireEvent("loadexception", this, null, trans.arg);
10566         trans.callback.call(trans.scope||window, null, trans.arg, false);
10567     }
10568 });/*
10569  * Based on:
10570  * Ext JS Library 1.1.1
10571  * Copyright(c) 2006-2007, Ext JS, LLC.
10572  *
10573  * Originally Released Under LGPL - original licence link has changed is not relivant.
10574  *
10575  * Fork - LGPL
10576  * <script type="text/javascript">
10577  */
10578
10579 /**
10580  * @class Roo.data.JsonReader
10581  * @extends Roo.data.DataReader
10582  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10583  * based on mappings in a provided Roo.data.Record constructor.
10584  * 
10585  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10586  * in the reply previously. 
10587  * 
10588  * <p>
10589  * Example code:
10590  * <pre><code>
10591 var RecordDef = Roo.data.Record.create([
10592     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10593     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10594 ]);
10595 var myReader = new Roo.data.JsonReader({
10596     totalProperty: "results",    // The property which contains the total dataset size (optional)
10597     root: "rows",                // The property which contains an Array of row objects
10598     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10599 }, RecordDef);
10600 </code></pre>
10601  * <p>
10602  * This would consume a JSON file like this:
10603  * <pre><code>
10604 { 'results': 2, 'rows': [
10605     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10606     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10607 }
10608 </code></pre>
10609  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10610  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10611  * paged from the remote server.
10612  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10613  * @cfg {String} root name of the property which contains the Array of row objects.
10614  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10615  * @constructor
10616  * Create a new JsonReader
10617  * @param {Object} meta Metadata configuration options
10618  * @param {Object} recordType Either an Array of field definition objects,
10619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10620  */
10621 Roo.data.JsonReader = function(meta, recordType){
10622     
10623     meta = meta || {};
10624     // set some defaults:
10625     Roo.applyIf(meta, {
10626         totalProperty: 'total',
10627         successProperty : 'success',
10628         root : 'data',
10629         id : 'id'
10630     });
10631     
10632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10633 };
10634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10635     
10636     /**
10637      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10638      * Used by Store query builder to append _requestMeta to params.
10639      * 
10640      */
10641     metaFromRemote : false,
10642     /**
10643      * This method is only used by a DataProxy which has retrieved data from a remote server.
10644      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10645      * @return {Object} data A data block which is used by an Roo.data.Store object as
10646      * a cache of Roo.data.Records.
10647      */
10648     read : function(response){
10649         var json = response.responseText;
10650        
10651         var o = /* eval:var:o */ eval("("+json+")");
10652         if(!o) {
10653             throw {message: "JsonReader.read: Json object not found"};
10654         }
10655         
10656         if(o.metaData){
10657             
10658             delete this.ef;
10659             this.metaFromRemote = true;
10660             this.meta = o.metaData;
10661             this.recordType = Roo.data.Record.create(o.metaData.fields);
10662             this.onMetaChange(this.meta, this.recordType, o);
10663         }
10664         return this.readRecords(o);
10665     },
10666
10667     // private function a store will implement
10668     onMetaChange : function(meta, recordType, o){
10669
10670     },
10671
10672     /**
10673          * @ignore
10674          */
10675     simpleAccess: function(obj, subsc) {
10676         return obj[subsc];
10677     },
10678
10679         /**
10680          * @ignore
10681          */
10682     getJsonAccessor: function(){
10683         var re = /[\[\.]/;
10684         return function(expr) {
10685             try {
10686                 return(re.test(expr))
10687                     ? new Function("obj", "return obj." + expr)
10688                     : function(obj){
10689                         return obj[expr];
10690                     };
10691             } catch(e){}
10692             return Roo.emptyFn;
10693         };
10694     }(),
10695
10696     /**
10697      * Create a data block containing Roo.data.Records from an XML document.
10698      * @param {Object} o An object which contains an Array of row objects in the property specified
10699      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10700      * which contains the total size of the dataset.
10701      * @return {Object} data A data block which is used by an Roo.data.Store object as
10702      * a cache of Roo.data.Records.
10703      */
10704     readRecords : function(o){
10705         /**
10706          * After any data loads, the raw JSON data is available for further custom processing.
10707          * @type Object
10708          */
10709         this.o = o;
10710         var s = this.meta, Record = this.recordType,
10711             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10712
10713 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10714         if (!this.ef) {
10715             if(s.totalProperty) {
10716                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10717                 }
10718                 if(s.successProperty) {
10719                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10720                 }
10721                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10722                 if (s.id) {
10723                         var g = this.getJsonAccessor(s.id);
10724                         this.getId = function(rec) {
10725                                 var r = g(rec);  
10726                                 return (r === undefined || r === "") ? null : r;
10727                         };
10728                 } else {
10729                         this.getId = function(){return null;};
10730                 }
10731             this.ef = [];
10732             for(var jj = 0; jj < fl; jj++){
10733                 f = fi[jj];
10734                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10735                 this.ef[jj] = this.getJsonAccessor(map);
10736             }
10737         }
10738
10739         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10740         if(s.totalProperty){
10741             var vt = parseInt(this.getTotal(o), 10);
10742             if(!isNaN(vt)){
10743                 totalRecords = vt;
10744             }
10745         }
10746         if(s.successProperty){
10747             var vs = this.getSuccess(o);
10748             if(vs === false || vs === 'false'){
10749                 success = false;
10750             }
10751         }
10752         var records = [];
10753         for(var i = 0; i < c; i++){
10754                 var n = root[i];
10755             var values = {};
10756             var id = this.getId(n);
10757             for(var j = 0; j < fl; j++){
10758                 f = fi[j];
10759             var v = this.ef[j](n);
10760             if (!f.convert) {
10761                 Roo.log('missing convert for ' + f.name);
10762                 Roo.log(f);
10763                 continue;
10764             }
10765             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10766             }
10767             var record = new Record(values, id);
10768             record.json = n;
10769             records[i] = record;
10770         }
10771         return {
10772             raw : o,
10773             success : success,
10774             records : records,
10775             totalRecords : totalRecords
10776         };
10777     }
10778 });/*
10779  * Based on:
10780  * Ext JS Library 1.1.1
10781  * Copyright(c) 2006-2007, Ext JS, LLC.
10782  *
10783  * Originally Released Under LGPL - original licence link has changed is not relivant.
10784  *
10785  * Fork - LGPL
10786  * <script type="text/javascript">
10787  */
10788
10789 /**
10790  * @class Roo.data.ArrayReader
10791  * @extends Roo.data.DataReader
10792  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10793  * Each element of that Array represents a row of data fields. The
10794  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10795  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10796  * <p>
10797  * Example code:.
10798  * <pre><code>
10799 var RecordDef = Roo.data.Record.create([
10800     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10801     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10802 ]);
10803 var myReader = new Roo.data.ArrayReader({
10804     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10805 }, RecordDef);
10806 </code></pre>
10807  * <p>
10808  * This would consume an Array like this:
10809  * <pre><code>
10810 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10811   </code></pre>
10812  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10813  * @constructor
10814  * Create a new JsonReader
10815  * @param {Object} meta Metadata configuration options.
10816  * @param {Object} recordType Either an Array of field definition objects
10817  * as specified to {@link Roo.data.Record#create},
10818  * or an {@link Roo.data.Record} object
10819  * created using {@link Roo.data.Record#create}.
10820  */
10821 Roo.data.ArrayReader = function(meta, recordType){
10822     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10823 };
10824
10825 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10826     /**
10827      * Create a data block containing Roo.data.Records from an XML document.
10828      * @param {Object} o An Array of row objects which represents the dataset.
10829      * @return {Object} data A data block which is used by an Roo.data.Store object as
10830      * a cache of Roo.data.Records.
10831      */
10832     readRecords : function(o){
10833         var sid = this.meta ? this.meta.id : null;
10834         var recordType = this.recordType, fields = recordType.prototype.fields;
10835         var records = [];
10836         var root = o;
10837             for(var i = 0; i < root.length; i++){
10838                     var n = root[i];
10839                 var values = {};
10840                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10841                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10842                 var f = fields.items[j];
10843                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10844                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10845                 v = f.convert(v);
10846                 values[f.name] = v;
10847             }
10848                 var record = new recordType(values, id);
10849                 record.json = n;
10850                 records[records.length] = record;
10851             }
10852             return {
10853                 records : records,
10854                 totalRecords : records.length
10855             };
10856     }
10857 });/*
10858  * - LGPL
10859  * * 
10860  */
10861
10862 /**
10863  * @class Roo.bootstrap.ComboBox
10864  * @extends Roo.bootstrap.TriggerField
10865  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10866  * @cfg {Boolean} append (true|false) default false
10867  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10868  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10869  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10870  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10871  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10872  * @constructor
10873  * Create a new ComboBox.
10874  * @param {Object} config Configuration options
10875  */
10876 Roo.bootstrap.ComboBox = function(config){
10877     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10878     this.addEvents({
10879         /**
10880          * @event expand
10881          * Fires when the dropdown list is expanded
10882              * @param {Roo.bootstrap.ComboBox} combo This combo box
10883              */
10884         'expand' : true,
10885         /**
10886          * @event collapse
10887          * Fires when the dropdown list is collapsed
10888              * @param {Roo.bootstrap.ComboBox} combo This combo box
10889              */
10890         'collapse' : true,
10891         /**
10892          * @event beforeselect
10893          * Fires before a list item is selected. Return false to cancel the selection.
10894              * @param {Roo.bootstrap.ComboBox} combo This combo box
10895              * @param {Roo.data.Record} record The data record returned from the underlying store
10896              * @param {Number} index The index of the selected item in the dropdown list
10897              */
10898         'beforeselect' : true,
10899         /**
10900          * @event select
10901          * Fires when a list item is selected
10902              * @param {Roo.bootstrap.ComboBox} combo This combo box
10903              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10904              * @param {Number} index The index of the selected item in the dropdown list
10905              */
10906         'select' : true,
10907         /**
10908          * @event beforequery
10909          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10910          * The event object passed has these properties:
10911              * @param {Roo.bootstrap.ComboBox} combo This combo box
10912              * @param {String} query The query
10913              * @param {Boolean} forceAll true to force "all" query
10914              * @param {Boolean} cancel true to cancel the query
10915              * @param {Object} e The query event object
10916              */
10917         'beforequery': true,
10918          /**
10919          * @event add
10920          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10921              * @param {Roo.bootstrap.ComboBox} combo This combo box
10922              */
10923         'add' : true,
10924         /**
10925          * @event edit
10926          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10927              * @param {Roo.bootstrap.ComboBox} combo This combo box
10928              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10929              */
10930         'edit' : true,
10931         /**
10932          * @event remove
10933          * Fires when the remove value from the combobox array
10934              * @param {Roo.bootstrap.ComboBox} combo This combo box
10935              */
10936         'remove' : true,
10937         /**
10938          * @event specialfilter
10939          * Fires when specialfilter
10940             * @param {Roo.bootstrap.ComboBox} combo This combo box
10941             */
10942         'specialfilter' : true
10943         
10944     });
10945     
10946     this.item = [];
10947     this.tickItems = [];
10948     
10949     this.selectedIndex = -1;
10950     if(this.mode == 'local'){
10951         if(config.queryDelay === undefined){
10952             this.queryDelay = 10;
10953         }
10954         if(config.minChars === undefined){
10955             this.minChars = 0;
10956         }
10957     }
10958 };
10959
10960 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10961      
10962     /**
10963      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10964      * rendering into an Roo.Editor, defaults to false)
10965      */
10966     /**
10967      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10968      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10969      */
10970     /**
10971      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10972      */
10973     /**
10974      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10975      * the dropdown list (defaults to undefined, with no header element)
10976      */
10977
10978      /**
10979      * @cfg {String/Roo.Template} tpl The template to use to render the output
10980      */
10981      
10982      /**
10983      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10984      */
10985     listWidth: undefined,
10986     /**
10987      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10988      * mode = 'remote' or 'text' if mode = 'local')
10989      */
10990     displayField: undefined,
10991     
10992     /**
10993      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10994      * mode = 'remote' or 'value' if mode = 'local'). 
10995      * Note: use of a valueField requires the user make a selection
10996      * in order for a value to be mapped.
10997      */
10998     valueField: undefined,
10999     
11000     
11001     /**
11002      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11003      * field's data value (defaults to the underlying DOM element's name)
11004      */
11005     hiddenName: undefined,
11006     /**
11007      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11008      */
11009     listClass: '',
11010     /**
11011      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11012      */
11013     selectedClass: 'active',
11014     
11015     /**
11016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11017      */
11018     shadow:'sides',
11019     /**
11020      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11021      * anchor positions (defaults to 'tl-bl')
11022      */
11023     listAlign: 'tl-bl?',
11024     /**
11025      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11026      */
11027     maxHeight: 300,
11028     /**
11029      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11030      * query specified by the allQuery config option (defaults to 'query')
11031      */
11032     triggerAction: 'query',
11033     /**
11034      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11035      * (defaults to 4, does not apply if editable = false)
11036      */
11037     minChars : 4,
11038     /**
11039      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11040      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11041      */
11042     typeAhead: false,
11043     /**
11044      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11045      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11046      */
11047     queryDelay: 500,
11048     /**
11049      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11050      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11051      */
11052     pageSize: 0,
11053     /**
11054      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11055      * when editable = true (defaults to false)
11056      */
11057     selectOnFocus:false,
11058     /**
11059      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11060      */
11061     queryParam: 'query',
11062     /**
11063      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11064      * when mode = 'remote' (defaults to 'Loading...')
11065      */
11066     loadingText: 'Loading...',
11067     /**
11068      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11069      */
11070     resizable: false,
11071     /**
11072      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11073      */
11074     handleHeight : 8,
11075     /**
11076      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11077      * traditional select (defaults to true)
11078      */
11079     editable: true,
11080     /**
11081      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11082      */
11083     allQuery: '',
11084     /**
11085      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11086      */
11087     mode: 'remote',
11088     /**
11089      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11090      * listWidth has a higher value)
11091      */
11092     minListWidth : 70,
11093     /**
11094      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11095      * allow the user to set arbitrary text into the field (defaults to false)
11096      */
11097     forceSelection:false,
11098     /**
11099      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11100      * if typeAhead = true (defaults to 250)
11101      */
11102     typeAheadDelay : 250,
11103     /**
11104      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11105      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11106      */
11107     valueNotFoundText : undefined,
11108     /**
11109      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11110      */
11111     blockFocus : false,
11112     
11113     /**
11114      * @cfg {Boolean} disableClear Disable showing of clear button.
11115      */
11116     disableClear : false,
11117     /**
11118      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11119      */
11120     alwaysQuery : false,
11121     
11122     /**
11123      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11124      */
11125     multiple : false,
11126     
11127     /**
11128      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11129      */
11130     invalidClass : "has-warning",
11131     
11132     /**
11133      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11134      */
11135     validClass : "has-success",
11136     
11137     /**
11138      * @cfg {Boolean} specialFilter (true|false) special filter default false
11139      */
11140     specialFilter : false,
11141     
11142     //private
11143     addicon : false,
11144     editicon: false,
11145     
11146     page: 0,
11147     hasQuery: false,
11148     append: false,
11149     loadNext: false,
11150     autoFocus : true,
11151     tickable : false,
11152     btnPosition : 'right',
11153     triggerList : true,
11154     showToggleBtn : true,
11155     // element that contains real text value.. (when hidden is used..)
11156     
11157     getAutoCreate : function()
11158     {
11159         var cfg = false;
11160         
11161         /*
11162          *  Normal ComboBox
11163          */
11164         if(!this.tickable){
11165             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11166             return cfg;
11167         }
11168         
11169         /*
11170          *  ComboBox with tickable selections
11171          */
11172              
11173         var align = this.labelAlign || this.parentLabelAlign();
11174         
11175         cfg = {
11176             cls : 'form-group roo-combobox-tickable' //input-group
11177         };
11178         
11179         var buttons = {
11180             tag : 'div',
11181             cls : 'tickable-buttons',
11182             cn : [
11183                 {
11184                     tag : 'button',
11185                     type : 'button',
11186                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11187                     html : 'Edit'
11188                 },
11189                 {
11190                     tag : 'button',
11191                     type : 'button',
11192                     name : 'ok',
11193                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11194                     html : 'Done'
11195                 },
11196                 {
11197                     tag : 'button',
11198                     type : 'button',
11199                     name : 'cancel',
11200                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11201                     html : 'Cancel'
11202                 }
11203             ]
11204         };
11205         
11206         if(this.editable){
11207             buttons.cn.unshift({
11208                 tag: 'input',
11209                 cls: 'select2-search-field-input'
11210             });
11211         }
11212         
11213         var _this = this;
11214         
11215         Roo.each(buttons.cn, function(c){
11216             if (_this.size) {
11217                 c.cls += ' btn-' + _this.size;
11218             }
11219
11220             if (_this.disabled) {
11221                 c.disabled = true;
11222             }
11223         });
11224         
11225         var box = {
11226             tag: 'div',
11227             cn: [
11228                 {
11229                     tag: 'input',
11230                     type : 'hidden',
11231                     cls: 'form-hidden-field'
11232                 },
11233                 {
11234                     tag: 'ul',
11235                     cls: 'select2-choices',
11236                     cn:[
11237                         {
11238                             tag: 'li',
11239                             cls: 'select2-search-field',
11240                             cn: [
11241
11242                                 buttons
11243                             ]
11244                         }
11245                     ]
11246                 }
11247             ]
11248         }
11249         
11250         var combobox = {
11251             cls: 'select2-container input-group select2-container-multi',
11252             cn: [
11253                 box
11254 //                {
11255 //                    tag: 'ul',
11256 //                    cls: 'typeahead typeahead-long dropdown-menu',
11257 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11258 //                }
11259             ]
11260         };
11261         
11262         if(this.hasFeedback && !this.allowBlank){
11263             
11264             var feedback = {
11265                 tag: 'span',
11266                 cls: 'glyphicon form-control-feedback'
11267             };
11268
11269             combobox.cn.push(feedback);
11270         }
11271         
11272         if (align ==='left' && this.fieldLabel.length) {
11273             
11274                 Roo.log("left and has label");
11275                 cfg.cn = [
11276                     
11277                     {
11278                         tag: 'label',
11279                         'for' :  id,
11280                         cls : 'control-label col-sm-' + this.labelWidth,
11281                         html : this.fieldLabel
11282                         
11283                     },
11284                     {
11285                         cls : "col-sm-" + (12 - this.labelWidth), 
11286                         cn: [
11287                             combobox
11288                         ]
11289                     }
11290                     
11291                 ];
11292         } else if ( this.fieldLabel.length) {
11293                 Roo.log(" label");
11294                  cfg.cn = [
11295                    
11296                     {
11297                         tag: 'label',
11298                         //cls : 'input-group-addon',
11299                         html : this.fieldLabel
11300                         
11301                     },
11302                     
11303                     combobox
11304                     
11305                 ];
11306
11307         } else {
11308             
11309                 Roo.log(" no label && no align");
11310                 cfg = combobox
11311                      
11312                 
11313         }
11314          
11315         var settings=this;
11316         ['xs','sm','md','lg'].map(function(size){
11317             if (settings[size]) {
11318                 cfg.cls += ' col-' + size + '-' + settings[size];
11319             }
11320         });
11321         
11322         return cfg;
11323         
11324     },
11325     
11326     // private
11327     initEvents: function()
11328     {
11329         
11330         if (!this.store) {
11331             throw "can not find store for combo";
11332         }
11333         this.store = Roo.factory(this.store, Roo.data);
11334         
11335         if(this.tickable){
11336             this.initTickableEvents();
11337             return;
11338         }
11339         
11340         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11341         
11342         if(this.hiddenName){
11343             
11344             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11345             
11346             this.hiddenField.dom.value =
11347                 this.hiddenValue !== undefined ? this.hiddenValue :
11348                 this.value !== undefined ? this.value : '';
11349
11350             // prevent input submission
11351             this.el.dom.removeAttribute('name');
11352             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11353              
11354              
11355         }
11356         //if(Roo.isGecko){
11357         //    this.el.dom.setAttribute('autocomplete', 'off');
11358         //}
11359         
11360         var cls = 'x-combo-list';
11361         
11362         //this.list = new Roo.Layer({
11363         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11364         //});
11365         
11366         var _this = this;
11367         
11368         (function(){
11369             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11370             _this.list.setWidth(lw);
11371         }).defer(100);
11372         
11373         this.list.on('mouseover', this.onViewOver, this);
11374         this.list.on('mousemove', this.onViewMove, this);
11375         
11376         this.list.on('scroll', this.onViewScroll, this);
11377         
11378         /*
11379         this.list.swallowEvent('mousewheel');
11380         this.assetHeight = 0;
11381
11382         if(this.title){
11383             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11384             this.assetHeight += this.header.getHeight();
11385         }
11386
11387         this.innerList = this.list.createChild({cls:cls+'-inner'});
11388         this.innerList.on('mouseover', this.onViewOver, this);
11389         this.innerList.on('mousemove', this.onViewMove, this);
11390         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11391         
11392         if(this.allowBlank && !this.pageSize && !this.disableClear){
11393             this.footer = this.list.createChild({cls:cls+'-ft'});
11394             this.pageTb = new Roo.Toolbar(this.footer);
11395            
11396         }
11397         if(this.pageSize){
11398             this.footer = this.list.createChild({cls:cls+'-ft'});
11399             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11400                     {pageSize: this.pageSize});
11401             
11402         }
11403         
11404         if (this.pageTb && this.allowBlank && !this.disableClear) {
11405             var _this = this;
11406             this.pageTb.add(new Roo.Toolbar.Fill(), {
11407                 cls: 'x-btn-icon x-btn-clear',
11408                 text: '&#160;',
11409                 handler: function()
11410                 {
11411                     _this.collapse();
11412                     _this.clearValue();
11413                     _this.onSelect(false, -1);
11414                 }
11415             });
11416         }
11417         if (this.footer) {
11418             this.assetHeight += this.footer.getHeight();
11419         }
11420         */
11421             
11422         if(!this.tpl){
11423             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11424         }
11425
11426         this.view = new Roo.View(this.list, this.tpl, {
11427             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11428         });
11429         //this.view.wrapEl.setDisplayed(false);
11430         this.view.on('click', this.onViewClick, this);
11431         
11432         
11433         
11434         this.store.on('beforeload', this.onBeforeLoad, this);
11435         this.store.on('load', this.onLoad, this);
11436         this.store.on('loadexception', this.onLoadException, this);
11437         /*
11438         if(this.resizable){
11439             this.resizer = new Roo.Resizable(this.list,  {
11440                pinned:true, handles:'se'
11441             });
11442             this.resizer.on('resize', function(r, w, h){
11443                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11444                 this.listWidth = w;
11445                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11446                 this.restrictHeight();
11447             }, this);
11448             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11449         }
11450         */
11451         if(!this.editable){
11452             this.editable = true;
11453             this.setEditable(false);
11454         }
11455         
11456         /*
11457         
11458         if (typeof(this.events.add.listeners) != 'undefined') {
11459             
11460             this.addicon = this.wrap.createChild(
11461                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11462        
11463             this.addicon.on('click', function(e) {
11464                 this.fireEvent('add', this);
11465             }, this);
11466         }
11467         if (typeof(this.events.edit.listeners) != 'undefined') {
11468             
11469             this.editicon = this.wrap.createChild(
11470                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11471             if (this.addicon) {
11472                 this.editicon.setStyle('margin-left', '40px');
11473             }
11474             this.editicon.on('click', function(e) {
11475                 
11476                 // we fire even  if inothing is selected..
11477                 this.fireEvent('edit', this, this.lastData );
11478                 
11479             }, this);
11480         }
11481         */
11482         
11483         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11484             "up" : function(e){
11485                 this.inKeyMode = true;
11486                 this.selectPrev();
11487             },
11488
11489             "down" : function(e){
11490                 if(!this.isExpanded()){
11491                     this.onTriggerClick();
11492                 }else{
11493                     this.inKeyMode = true;
11494                     this.selectNext();
11495                 }
11496             },
11497
11498             "enter" : function(e){
11499 //                this.onViewClick();
11500                 //return true;
11501                 this.collapse();
11502                 
11503                 if(this.fireEvent("specialkey", this, e)){
11504                     this.onViewClick(false);
11505                 }
11506                 
11507                 return true;
11508             },
11509
11510             "esc" : function(e){
11511                 this.collapse();
11512             },
11513
11514             "tab" : function(e){
11515                 this.collapse();
11516                 
11517                 if(this.fireEvent("specialkey", this, e)){
11518                     this.onViewClick(false);
11519                 }
11520                 
11521                 return true;
11522             },
11523
11524             scope : this,
11525
11526             doRelay : function(foo, bar, hname){
11527                 if(hname == 'down' || this.scope.isExpanded()){
11528                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11529                 }
11530                 return true;
11531             },
11532
11533             forceKeyDown: true
11534         });
11535         
11536         
11537         this.queryDelay = Math.max(this.queryDelay || 10,
11538                 this.mode == 'local' ? 10 : 250);
11539         
11540         
11541         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11542         
11543         if(this.typeAhead){
11544             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11545         }
11546         if(this.editable !== false){
11547             this.inputEl().on("keyup", this.onKeyUp, this);
11548         }
11549         if(this.forceSelection){
11550             this.inputEl().on('blur', this.doForce, this);
11551         }
11552         
11553         if(this.multiple){
11554             this.choices = this.el.select('ul.select2-choices', true).first();
11555             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11556         }
11557     },
11558     
11559     initTickableEvents: function()
11560     {   
11561         this.createList();
11562         
11563         if(this.hiddenName){
11564             
11565             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11566             
11567             this.hiddenField.dom.value =
11568                 this.hiddenValue !== undefined ? this.hiddenValue :
11569                 this.value !== undefined ? this.value : '';
11570
11571             // prevent input submission
11572             this.el.dom.removeAttribute('name');
11573             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11574              
11575              
11576         }
11577         
11578 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11579         
11580         this.choices = this.el.select('ul.select2-choices', true).first();
11581         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11582         if(this.triggerList){
11583             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11584         }
11585          
11586         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11587         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11588         
11589         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11590         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11591         
11592         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11593         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11594         
11595         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11596         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11597         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11598         
11599         this.okBtn.hide();
11600         this.cancelBtn.hide();
11601         
11602         var _this = this;
11603         
11604         (function(){
11605             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11606             _this.list.setWidth(lw);
11607         }).defer(100);
11608         
11609         this.list.on('mouseover', this.onViewOver, this);
11610         this.list.on('mousemove', this.onViewMove, this);
11611         
11612         this.list.on('scroll', this.onViewScroll, this);
11613         
11614         if(!this.tpl){
11615             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>';
11616         }
11617
11618         this.view = new Roo.View(this.list, this.tpl, {
11619             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11620         });
11621         
11622         //this.view.wrapEl.setDisplayed(false);
11623         this.view.on('click', this.onViewClick, this);
11624         
11625         
11626         
11627         this.store.on('beforeload', this.onBeforeLoad, this);
11628         this.store.on('load', this.onLoad, this);
11629         this.store.on('loadexception', this.onLoadException, this);
11630         
11631         if(this.editable){
11632             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11633                 "up" : function(e){
11634                     this.inKeyMode = true;
11635                     this.selectPrev();
11636                 },
11637
11638                 "down" : function(e){
11639                     this.inKeyMode = true;
11640                     this.selectNext();
11641                 },
11642
11643                 "enter" : function(e){
11644                     if(this.fireEvent("specialkey", this, e)){
11645                         this.onViewClick(false);
11646                     }
11647                     
11648                     return true;
11649                 },
11650
11651                 "esc" : function(e){
11652                     this.onTickableFooterButtonClick(e, false, false);
11653                 },
11654
11655                 "tab" : function(e){
11656                     this.fireEvent("specialkey", this, e);
11657                     
11658                     this.onTickableFooterButtonClick(e, false, false);
11659                     
11660                     return true;
11661                 },
11662
11663                 scope : this,
11664
11665                 doRelay : function(e, fn, key){
11666                     if(this.scope.isExpanded()){
11667                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11668                     }
11669                     return true;
11670                 },
11671
11672                 forceKeyDown: true
11673             });
11674         }
11675         
11676         this.queryDelay = Math.max(this.queryDelay || 10,
11677                 this.mode == 'local' ? 10 : 250);
11678         
11679         
11680         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11681         
11682         if(this.typeAhead){
11683             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11684         }
11685         
11686         if(this.editable !== false){
11687             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11688         }
11689         
11690     },
11691
11692     onDestroy : function(){
11693         if(this.view){
11694             this.view.setStore(null);
11695             this.view.el.removeAllListeners();
11696             this.view.el.remove();
11697             this.view.purgeListeners();
11698         }
11699         if(this.list){
11700             this.list.dom.innerHTML  = '';
11701         }
11702         
11703         if(this.store){
11704             this.store.un('beforeload', this.onBeforeLoad, this);
11705             this.store.un('load', this.onLoad, this);
11706             this.store.un('loadexception', this.onLoadException, this);
11707         }
11708         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11709     },
11710
11711     // private
11712     fireKey : function(e){
11713         if(e.isNavKeyPress() && !this.list.isVisible()){
11714             this.fireEvent("specialkey", this, e);
11715         }
11716     },
11717
11718     // private
11719     onResize: function(w, h){
11720 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11721 //        
11722 //        if(typeof w != 'number'){
11723 //            // we do not handle it!?!?
11724 //            return;
11725 //        }
11726 //        var tw = this.trigger.getWidth();
11727 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11728 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11729 //        var x = w - tw;
11730 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11731 //            
11732 //        //this.trigger.setStyle('left', x+'px');
11733 //        
11734 //        if(this.list && this.listWidth === undefined){
11735 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11736 //            this.list.setWidth(lw);
11737 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11738 //        }
11739         
11740     
11741         
11742     },
11743
11744     /**
11745      * Allow or prevent the user from directly editing the field text.  If false is passed,
11746      * the user will only be able to select from the items defined in the dropdown list.  This method
11747      * is the runtime equivalent of setting the 'editable' config option at config time.
11748      * @param {Boolean} value True to allow the user to directly edit the field text
11749      */
11750     setEditable : function(value){
11751         if(value == this.editable){
11752             return;
11753         }
11754         this.editable = value;
11755         if(!value){
11756             this.inputEl().dom.setAttribute('readOnly', true);
11757             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11758             this.inputEl().addClass('x-combo-noedit');
11759         }else{
11760             this.inputEl().dom.setAttribute('readOnly', false);
11761             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11762             this.inputEl().removeClass('x-combo-noedit');
11763         }
11764     },
11765
11766     // private
11767     
11768     onBeforeLoad : function(combo,opts){
11769         if(!this.hasFocus){
11770             return;
11771         }
11772          if (!opts.add) {
11773             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11774          }
11775         this.restrictHeight();
11776         this.selectedIndex = -1;
11777     },
11778
11779     // private
11780     onLoad : function(){
11781         
11782         this.hasQuery = false;
11783         
11784         if(!this.hasFocus){
11785             return;
11786         }
11787         
11788         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11789             this.loading.hide();
11790         }
11791              
11792         if(this.store.getCount() > 0){
11793             this.expand();
11794             this.restrictHeight();
11795             if(this.lastQuery == this.allQuery){
11796                 if(this.editable && !this.tickable){
11797                     this.inputEl().dom.select();
11798                 }
11799                 
11800                 if(
11801                     !this.selectByValue(this.value, true) &&
11802                     this.autoFocus && 
11803                     (
11804                         !this.store.lastOptions ||
11805                         typeof(this.store.lastOptions.add) == 'undefined' || 
11806                         this.store.lastOptions.add != true
11807                     )
11808                 ){
11809                     this.select(0, true);
11810                 }
11811             }else{
11812                 if(this.autoFocus){
11813                     this.selectNext();
11814                 }
11815                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11816                     this.taTask.delay(this.typeAheadDelay);
11817                 }
11818             }
11819         }else{
11820             this.onEmptyResults();
11821         }
11822         
11823         //this.el.focus();
11824     },
11825     // private
11826     onLoadException : function()
11827     {
11828         this.hasQuery = false;
11829         
11830         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11831             this.loading.hide();
11832         }
11833         
11834         if(this.tickable && this.editable){
11835             return;
11836         }
11837         
11838         this.collapse();
11839         
11840         Roo.log(this.store.reader.jsonData);
11841         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11842             // fixme
11843             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11844         }
11845         
11846         
11847     },
11848     // private
11849     onTypeAhead : function(){
11850         if(this.store.getCount() > 0){
11851             var r = this.store.getAt(0);
11852             var newValue = r.data[this.displayField];
11853             var len = newValue.length;
11854             var selStart = this.getRawValue().length;
11855             
11856             if(selStart != len){
11857                 this.setRawValue(newValue);
11858                 this.selectText(selStart, newValue.length);
11859             }
11860         }
11861     },
11862
11863     // private
11864     onSelect : function(record, index){
11865         
11866         if(this.fireEvent('beforeselect', this, record, index) !== false){
11867         
11868             this.setFromData(index > -1 ? record.data : false);
11869             
11870             this.collapse();
11871             this.fireEvent('select', this, record, index);
11872         }
11873     },
11874
11875     /**
11876      * Returns the currently selected field value or empty string if no value is set.
11877      * @return {String} value The selected value
11878      */
11879     getValue : function(){
11880         
11881         if(this.multiple){
11882             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11883         }
11884         
11885         if(this.valueField){
11886             return typeof this.value != 'undefined' ? this.value : '';
11887         }else{
11888             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11889         }
11890     },
11891
11892     /**
11893      * Clears any text/value currently set in the field
11894      */
11895     clearValue : function(){
11896         if(this.hiddenField){
11897             this.hiddenField.dom.value = '';
11898         }
11899         this.value = '';
11900         this.setRawValue('');
11901         this.lastSelectionText = '';
11902         this.lastData = false;
11903         
11904     },
11905
11906     /**
11907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11908      * will be displayed in the field.  If the value does not match the data value of an existing item,
11909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11910      * Otherwise the field will be blank (although the value will still be set).
11911      * @param {String} value The value to match
11912      */
11913     setValue : function(v){
11914         if(this.multiple){
11915             this.syncValue();
11916             return;
11917         }
11918         
11919         var text = v;
11920         if(this.valueField){
11921             var r = this.findRecord(this.valueField, v);
11922             if(r){
11923                 text = r.data[this.displayField];
11924             }else if(this.valueNotFoundText !== undefined){
11925                 text = this.valueNotFoundText;
11926             }
11927         }
11928         this.lastSelectionText = text;
11929         if(this.hiddenField){
11930             this.hiddenField.dom.value = v;
11931         }
11932         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11933         this.value = v;
11934     },
11935     /**
11936      * @property {Object} the last set data for the element
11937      */
11938     
11939     lastData : false,
11940     /**
11941      * Sets the value of the field based on a object which is related to the record format for the store.
11942      * @param {Object} value the value to set as. or false on reset?
11943      */
11944     setFromData : function(o){
11945         
11946         if(this.multiple){
11947             this.addItem(o);
11948             return;
11949         }
11950             
11951         var dv = ''; // display value
11952         var vv = ''; // value value..
11953         this.lastData = o;
11954         if (this.displayField) {
11955             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11956         } else {
11957             // this is an error condition!!!
11958             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11959         }
11960         
11961         if(this.valueField){
11962             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11963         }
11964         
11965         if(this.hiddenField){
11966             this.hiddenField.dom.value = vv;
11967             
11968             this.lastSelectionText = dv;
11969             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11970             this.value = vv;
11971             return;
11972         }
11973         // no hidden field.. - we store the value in 'value', but still display
11974         // display field!!!!
11975         this.lastSelectionText = dv;
11976         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11977         this.value = vv;
11978         
11979         
11980     },
11981     // private
11982     reset : function(){
11983         // overridden so that last data is reset..
11984         
11985         if(this.multiple){
11986             this.clearItem();
11987             return;
11988         }
11989         
11990         this.setValue(this.originalValue);
11991         this.clearInvalid();
11992         this.lastData = false;
11993         if (this.view) {
11994             this.view.clearSelections();
11995         }
11996     },
11997     // private
11998     findRecord : function(prop, value){
11999         var record;
12000         if(this.store.getCount() > 0){
12001             this.store.each(function(r){
12002                 if(r.data[prop] == value){
12003                     record = r;
12004                     return false;
12005                 }
12006                 return true;
12007             });
12008         }
12009         return record;
12010     },
12011     
12012     getName: function()
12013     {
12014         // returns hidden if it's set..
12015         if (!this.rendered) {return ''};
12016         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12017         
12018     },
12019     // private
12020     onViewMove : function(e, t){
12021         this.inKeyMode = false;
12022     },
12023
12024     // private
12025     onViewOver : function(e, t){
12026         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12027             return;
12028         }
12029         var item = this.view.findItemFromChild(t);
12030         
12031         if(item){
12032             var index = this.view.indexOf(item);
12033             this.select(index, false);
12034         }
12035     },
12036
12037     // private
12038     onViewClick : function(view, doFocus, el, e)
12039     {
12040         var index = this.view.getSelectedIndexes()[0];
12041         
12042         var r = this.store.getAt(index);
12043         
12044         if(this.tickable){
12045             
12046             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12047                 return;
12048             }
12049             
12050             var rm = false;
12051             var _this = this;
12052             
12053             Roo.each(this.tickItems, function(v,k){
12054                 
12055                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12056                     _this.tickItems.splice(k, 1);
12057                     
12058                     if(typeof(e) == 'undefined' && view == false){
12059                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12060                     }
12061                     
12062                     rm = true;
12063                     return;
12064                 }
12065             });
12066             
12067             if(rm){
12068                 return;
12069             }
12070             
12071             this.tickItems.push(r.data);
12072             
12073             if(typeof(e) == 'undefined' && view == false){
12074                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12075             }
12076                     
12077             return;
12078         }
12079         
12080         if(r){
12081             this.onSelect(r, index);
12082         }
12083         if(doFocus !== false && !this.blockFocus){
12084             this.inputEl().focus();
12085         }
12086     },
12087
12088     // private
12089     restrictHeight : function(){
12090         //this.innerList.dom.style.height = '';
12091         //var inner = this.innerList.dom;
12092         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12093         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12094         //this.list.beginUpdate();
12095         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12096         this.list.alignTo(this.inputEl(), this.listAlign);
12097         this.list.alignTo(this.inputEl(), this.listAlign);
12098         //this.list.endUpdate();
12099     },
12100
12101     // private
12102     onEmptyResults : function(){
12103         
12104         if(this.tickable && this.editable){
12105             this.restrictHeight();
12106             return;
12107         }
12108         
12109         this.collapse();
12110     },
12111
12112     /**
12113      * Returns true if the dropdown list is expanded, else false.
12114      */
12115     isExpanded : function(){
12116         return this.list.isVisible();
12117     },
12118
12119     /**
12120      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12121      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12122      * @param {String} value The data value of the item to select
12123      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12124      * selected item if it is not currently in view (defaults to true)
12125      * @return {Boolean} True if the value matched an item in the list, else false
12126      */
12127     selectByValue : function(v, scrollIntoView){
12128         if(v !== undefined && v !== null){
12129             var r = this.findRecord(this.valueField || this.displayField, v);
12130             if(r){
12131                 this.select(this.store.indexOf(r), scrollIntoView);
12132                 return true;
12133             }
12134         }
12135         return false;
12136     },
12137
12138     /**
12139      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12140      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12141      * @param {Number} index The zero-based index of the list item to select
12142      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12143      * selected item if it is not currently in view (defaults to true)
12144      */
12145     select : function(index, scrollIntoView){
12146         this.selectedIndex = index;
12147         this.view.select(index);
12148         if(scrollIntoView !== false){
12149             var el = this.view.getNode(index);
12150             /*
12151              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12152              */
12153             if(el){
12154                 this.list.scrollChildIntoView(el, false);
12155             }
12156         }
12157     },
12158
12159     // private
12160     selectNext : function(){
12161         var ct = this.store.getCount();
12162         if(ct > 0){
12163             if(this.selectedIndex == -1){
12164                 this.select(0);
12165             }else if(this.selectedIndex < ct-1){
12166                 this.select(this.selectedIndex+1);
12167             }
12168         }
12169     },
12170
12171     // private
12172     selectPrev : function(){
12173         var ct = this.store.getCount();
12174         if(ct > 0){
12175             if(this.selectedIndex == -1){
12176                 this.select(0);
12177             }else if(this.selectedIndex != 0){
12178                 this.select(this.selectedIndex-1);
12179             }
12180         }
12181     },
12182
12183     // private
12184     onKeyUp : function(e){
12185         if(this.editable !== false && !e.isSpecialKey()){
12186             this.lastKey = e.getKey();
12187             this.dqTask.delay(this.queryDelay);
12188         }
12189     },
12190
12191     // private
12192     validateBlur : function(){
12193         return !this.list || !this.list.isVisible();   
12194     },
12195
12196     // private
12197     initQuery : function(){
12198         
12199         var v = this.getRawValue();
12200         
12201         if(this.tickable && this.editable){
12202             v = this.tickableInputEl().getValue();
12203         }
12204         
12205         this.doQuery(v);
12206     },
12207
12208     // private
12209     doForce : function(){
12210         if(this.inputEl().dom.value.length > 0){
12211             this.inputEl().dom.value =
12212                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12213              
12214         }
12215     },
12216
12217     /**
12218      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12219      * query allowing the query action to be canceled if needed.
12220      * @param {String} query The SQL query to execute
12221      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12222      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12223      * saved in the current store (defaults to false)
12224      */
12225     doQuery : function(q, forceAll){
12226         
12227         if(q === undefined || q === null){
12228             q = '';
12229         }
12230         var qe = {
12231             query: q,
12232             forceAll: forceAll,
12233             combo: this,
12234             cancel:false
12235         };
12236         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12237             return false;
12238         }
12239         q = qe.query;
12240         
12241         forceAll = qe.forceAll;
12242         if(forceAll === true || (q.length >= this.minChars)){
12243             
12244             this.hasQuery = true;
12245             
12246             if(this.lastQuery != q || this.alwaysQuery){
12247                 this.lastQuery = q;
12248                 if(this.mode == 'local'){
12249                     this.selectedIndex = -1;
12250                     if(forceAll){
12251                         this.store.clearFilter();
12252                     }else{
12253                         
12254                         if(this.specialFilter){
12255                             this.fireEvent('specialfilter', this);
12256                             this.onLoad();
12257                             return;
12258                         }
12259                         
12260                         this.store.filter(this.displayField, q);
12261                     }
12262                     
12263                     this.store.fireEvent("datachanged", this.store);
12264                     
12265                     this.onLoad();
12266                     
12267                     
12268                 }else{
12269                     
12270                     this.store.baseParams[this.queryParam] = q;
12271                     
12272                     var options = {params : this.getParams(q)};
12273                     
12274                     if(this.loadNext){
12275                         options.add = true;
12276                         options.params.start = this.page * this.pageSize;
12277                     }
12278                     
12279                     this.store.load(options);
12280                     
12281                     /*
12282                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12283                      *  we should expand the list on onLoad
12284                      *  so command out it
12285                      */
12286 //                    this.expand();
12287                 }
12288             }else{
12289                 this.selectedIndex = -1;
12290                 this.onLoad();   
12291             }
12292         }
12293         
12294         this.loadNext = false;
12295     },
12296     
12297     // private
12298     getParams : function(q){
12299         var p = {};
12300         //p[this.queryParam] = q;
12301         
12302         if(this.pageSize){
12303             p.start = 0;
12304             p.limit = this.pageSize;
12305         }
12306         return p;
12307     },
12308
12309     /**
12310      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12311      */
12312     collapse : function(){
12313         if(!this.isExpanded()){
12314             return;
12315         }
12316         
12317         this.list.hide();
12318         
12319         if(this.tickable){
12320             this.hasFocus = false;
12321             this.okBtn.hide();
12322             this.cancelBtn.hide();
12323             this.trigger.show();
12324             
12325             if(this.editable){
12326                 this.tickableInputEl().dom.value = '';
12327                 this.tickableInputEl().blur();
12328             }
12329             
12330         }
12331         
12332         Roo.get(document).un('mousedown', this.collapseIf, this);
12333         Roo.get(document).un('mousewheel', this.collapseIf, this);
12334         if (!this.editable) {
12335             Roo.get(document).un('keydown', this.listKeyPress, this);
12336         }
12337         this.fireEvent('collapse', this);
12338     },
12339
12340     // private
12341     collapseIf : function(e){
12342         var in_combo  = e.within(this.el);
12343         var in_list =  e.within(this.list);
12344         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12345         
12346         if (in_combo || in_list || is_list) {
12347             //e.stopPropagation();
12348             return;
12349         }
12350         
12351         if(this.tickable){
12352             this.onTickableFooterButtonClick(e, false, false);
12353         }
12354
12355         this.collapse();
12356         
12357     },
12358
12359     /**
12360      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12361      */
12362     expand : function(){
12363        
12364         if(this.isExpanded() || !this.hasFocus){
12365             return;
12366         }
12367         
12368         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12369         this.list.setWidth(lw);
12370         
12371         
12372          Roo.log('expand');
12373         
12374         this.list.show();
12375         
12376         this.restrictHeight();
12377         
12378         if(this.tickable){
12379             
12380             this.tickItems = Roo.apply([], this.item);
12381             
12382             this.okBtn.show();
12383             this.cancelBtn.show();
12384             this.trigger.hide();
12385             
12386             if(this.editable){
12387                 this.tickableInputEl().focus();
12388             }
12389             
12390         }
12391         
12392         Roo.get(document).on('mousedown', this.collapseIf, this);
12393         Roo.get(document).on('mousewheel', this.collapseIf, this);
12394         if (!this.editable) {
12395             Roo.get(document).on('keydown', this.listKeyPress, this);
12396         }
12397         
12398         this.fireEvent('expand', this);
12399     },
12400
12401     // private
12402     // Implements the default empty TriggerField.onTriggerClick function
12403     onTriggerClick : function(e)
12404     {
12405         Roo.log('trigger click');
12406         
12407         if(this.disabled || !this.triggerList){
12408             return;
12409         }
12410         
12411         this.page = 0;
12412         this.loadNext = false;
12413         
12414         if(this.isExpanded()){
12415             this.collapse();
12416             if (!this.blockFocus) {
12417                 this.inputEl().focus();
12418             }
12419             
12420         }else {
12421             this.hasFocus = true;
12422             if(this.triggerAction == 'all') {
12423                 this.doQuery(this.allQuery, true);
12424             } else {
12425                 this.doQuery(this.getRawValue());
12426             }
12427             if (!this.blockFocus) {
12428                 this.inputEl().focus();
12429             }
12430         }
12431     },
12432     
12433     onTickableTriggerClick : function(e)
12434     {
12435         if(this.disabled){
12436             return;
12437         }
12438         
12439         this.page = 0;
12440         this.loadNext = false;
12441         this.hasFocus = true;
12442         
12443         if(this.triggerAction == 'all') {
12444             this.doQuery(this.allQuery, true);
12445         } else {
12446             this.doQuery(this.getRawValue());
12447         }
12448     },
12449     
12450     onSearchFieldClick : function(e)
12451     {
12452         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12453             this.onTickableFooterButtonClick(e, false, false);
12454             return;
12455         }
12456         
12457         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12458             return;
12459         }
12460         
12461         this.page = 0;
12462         this.loadNext = false;
12463         this.hasFocus = true;
12464         
12465         if(this.triggerAction == 'all') {
12466             this.doQuery(this.allQuery, true);
12467         } else {
12468             this.doQuery(this.getRawValue());
12469         }
12470     },
12471     
12472     listKeyPress : function(e)
12473     {
12474         //Roo.log('listkeypress');
12475         // scroll to first matching element based on key pres..
12476         if (e.isSpecialKey()) {
12477             return false;
12478         }
12479         var k = String.fromCharCode(e.getKey()).toUpperCase();
12480         //Roo.log(k);
12481         var match  = false;
12482         var csel = this.view.getSelectedNodes();
12483         var cselitem = false;
12484         if (csel.length) {
12485             var ix = this.view.indexOf(csel[0]);
12486             cselitem  = this.store.getAt(ix);
12487             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12488                 cselitem = false;
12489             }
12490             
12491         }
12492         
12493         this.store.each(function(v) { 
12494             if (cselitem) {
12495                 // start at existing selection.
12496                 if (cselitem.id == v.id) {
12497                     cselitem = false;
12498                 }
12499                 return true;
12500             }
12501                 
12502             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12503                 match = this.store.indexOf(v);
12504                 return false;
12505             }
12506             return true;
12507         }, this);
12508         
12509         if (match === false) {
12510             return true; // no more action?
12511         }
12512         // scroll to?
12513         this.view.select(match);
12514         var sn = Roo.get(this.view.getSelectedNodes()[0])
12515         sn.scrollIntoView(sn.dom.parentNode, false);
12516     },
12517     
12518     onViewScroll : function(e, t){
12519         
12520         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){
12521             return;
12522         }
12523         
12524         this.hasQuery = true;
12525         
12526         this.loading = this.list.select('.loading', true).first();
12527         
12528         if(this.loading === null){
12529             this.list.createChild({
12530                 tag: 'div',
12531                 cls: 'loading select2-more-results select2-active',
12532                 html: 'Loading more results...'
12533             })
12534             
12535             this.loading = this.list.select('.loading', true).first();
12536             
12537             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12538             
12539             this.loading.hide();
12540         }
12541         
12542         this.loading.show();
12543         
12544         var _combo = this;
12545         
12546         this.page++;
12547         this.loadNext = true;
12548         
12549         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12550         
12551         return;
12552     },
12553     
12554     addItem : function(o)
12555     {   
12556         var dv = ''; // display value
12557         
12558         if (this.displayField) {
12559             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12560         } else {
12561             // this is an error condition!!!
12562             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12563         }
12564         
12565         if(!dv.length){
12566             return;
12567         }
12568         
12569         var choice = this.choices.createChild({
12570             tag: 'li',
12571             cls: 'select2-search-choice',
12572             cn: [
12573                 {
12574                     tag: 'div',
12575                     html: dv
12576                 },
12577                 {
12578                     tag: 'a',
12579                     href: '#',
12580                     cls: 'select2-search-choice-close',
12581                     tabindex: '-1'
12582                 }
12583             ]
12584             
12585         }, this.searchField);
12586         
12587         var close = choice.select('a.select2-search-choice-close', true).first()
12588         
12589         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12590         
12591         this.item.push(o);
12592         
12593         this.lastData = o;
12594         
12595         this.syncValue();
12596         
12597         this.inputEl().dom.value = '';
12598         
12599         this.validate();
12600     },
12601     
12602     onRemoveItem : function(e, _self, o)
12603     {
12604         e.preventDefault();
12605         
12606         this.lastItem = Roo.apply([], this.item);
12607         
12608         var index = this.item.indexOf(o.data) * 1;
12609         
12610         if( index < 0){
12611             Roo.log('not this item?!');
12612             return;
12613         }
12614         
12615         this.item.splice(index, 1);
12616         o.item.remove();
12617         
12618         this.syncValue();
12619         
12620         this.fireEvent('remove', this, e);
12621         
12622         this.validate();
12623         
12624     },
12625     
12626     syncValue : function()
12627     {
12628         if(!this.item.length){
12629             this.clearValue();
12630             return;
12631         }
12632             
12633         var value = [];
12634         var _this = this;
12635         Roo.each(this.item, function(i){
12636             if(_this.valueField){
12637                 value.push(i[_this.valueField]);
12638                 return;
12639             }
12640
12641             value.push(i);
12642         });
12643
12644         this.value = value.join(',');
12645
12646         if(this.hiddenField){
12647             this.hiddenField.dom.value = this.value;
12648         }
12649         
12650         this.store.fireEvent("datachanged", this.store);
12651     },
12652     
12653     clearItem : function()
12654     {
12655         if(!this.multiple){
12656             return;
12657         }
12658         
12659         this.item = [];
12660         
12661         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12662            c.remove();
12663         });
12664         
12665         this.syncValue();
12666         
12667         this.validate();
12668     },
12669     
12670     inputEl: function ()
12671     {
12672         if(this.tickable){
12673             return this.searchField;
12674         }
12675         return this.el.select('input.form-control',true).first();
12676     },
12677     
12678     
12679     onTickableFooterButtonClick : function(e, btn, el)
12680     {
12681         e.preventDefault();
12682         
12683         this.lastItem = Roo.apply([], this.item);
12684         
12685         if(btn && btn.name == 'cancel'){
12686             this.tickItems = Roo.apply([], this.item);
12687             this.collapse();
12688             return;
12689         }
12690         
12691         this.clearItem();
12692         
12693         var _this = this;
12694         
12695         Roo.each(this.tickItems, function(o){
12696             _this.addItem(o);
12697         });
12698         
12699         this.collapse();
12700         
12701     },
12702     
12703     validate : function()
12704     {
12705         var v = this.getRawValue();
12706         
12707         if(this.multiple){
12708             v = this.getValue();
12709         }
12710         
12711         if(this.disabled || this.allowBlank || v.length){
12712             this.markValid();
12713             return true;
12714         }
12715         
12716         this.markInvalid();
12717         return false;
12718     },
12719     
12720     tickableInputEl : function()
12721     {
12722         if(!this.tickable || !this.editable){
12723             return this.inputEl();
12724         }
12725         
12726         return this.inputEl().select('.select2-search-field-input', true).first();
12727     }
12728     
12729     
12730
12731     /** 
12732     * @cfg {Boolean} grow 
12733     * @hide 
12734     */
12735     /** 
12736     * @cfg {Number} growMin 
12737     * @hide 
12738     */
12739     /** 
12740     * @cfg {Number} growMax 
12741     * @hide 
12742     */
12743     /**
12744      * @hide
12745      * @method autoSize
12746      */
12747 });
12748 /*
12749  * Based on:
12750  * Ext JS Library 1.1.1
12751  * Copyright(c) 2006-2007, Ext JS, LLC.
12752  *
12753  * Originally Released Under LGPL - original licence link has changed is not relivant.
12754  *
12755  * Fork - LGPL
12756  * <script type="text/javascript">
12757  */
12758
12759 /**
12760  * @class Roo.View
12761  * @extends Roo.util.Observable
12762  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12763  * This class also supports single and multi selection modes. <br>
12764  * Create a data model bound view:
12765  <pre><code>
12766  var store = new Roo.data.Store(...);
12767
12768  var view = new Roo.View({
12769     el : "my-element",
12770     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12771  
12772     singleSelect: true,
12773     selectedClass: "ydataview-selected",
12774     store: store
12775  });
12776
12777  // listen for node click?
12778  view.on("click", function(vw, index, node, e){
12779  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12780  });
12781
12782  // load XML data
12783  dataModel.load("foobar.xml");
12784  </code></pre>
12785  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12786  * <br><br>
12787  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12788  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12789  * 
12790  * Note: old style constructor is still suported (container, template, config)
12791  * 
12792  * @constructor
12793  * Create a new View
12794  * @param {Object} config The config object
12795  * 
12796  */
12797 Roo.View = function(config, depreciated_tpl, depreciated_config){
12798     
12799     this.parent = false;
12800     
12801     if (typeof(depreciated_tpl) == 'undefined') {
12802         // new way.. - universal constructor.
12803         Roo.apply(this, config);
12804         this.el  = Roo.get(this.el);
12805     } else {
12806         // old format..
12807         this.el  = Roo.get(config);
12808         this.tpl = depreciated_tpl;
12809         Roo.apply(this, depreciated_config);
12810     }
12811     this.wrapEl  = this.el.wrap().wrap();
12812     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12813     
12814     
12815     if(typeof(this.tpl) == "string"){
12816         this.tpl = new Roo.Template(this.tpl);
12817     } else {
12818         // support xtype ctors..
12819         this.tpl = new Roo.factory(this.tpl, Roo);
12820     }
12821     
12822     
12823     this.tpl.compile();
12824     
12825     /** @private */
12826     this.addEvents({
12827         /**
12828          * @event beforeclick
12829          * Fires before a click is processed. Returns false to cancel the default action.
12830          * @param {Roo.View} this
12831          * @param {Number} index The index of the target node
12832          * @param {HTMLElement} node The target node
12833          * @param {Roo.EventObject} e The raw event object
12834          */
12835             "beforeclick" : true,
12836         /**
12837          * @event click
12838          * Fires when a template node is clicked.
12839          * @param {Roo.View} this
12840          * @param {Number} index The index of the target node
12841          * @param {HTMLElement} node The target node
12842          * @param {Roo.EventObject} e The raw event object
12843          */
12844             "click" : true,
12845         /**
12846          * @event dblclick
12847          * Fires when a template node is double clicked.
12848          * @param {Roo.View} this
12849          * @param {Number} index The index of the target node
12850          * @param {HTMLElement} node The target node
12851          * @param {Roo.EventObject} e The raw event object
12852          */
12853             "dblclick" : true,
12854         /**
12855          * @event contextmenu
12856          * Fires when a template node is right clicked.
12857          * @param {Roo.View} this
12858          * @param {Number} index The index of the target node
12859          * @param {HTMLElement} node The target node
12860          * @param {Roo.EventObject} e The raw event object
12861          */
12862             "contextmenu" : true,
12863         /**
12864          * @event selectionchange
12865          * Fires when the selected nodes change.
12866          * @param {Roo.View} this
12867          * @param {Array} selections Array of the selected nodes
12868          */
12869             "selectionchange" : true,
12870     
12871         /**
12872          * @event beforeselect
12873          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12874          * @param {Roo.View} this
12875          * @param {HTMLElement} node The node to be selected
12876          * @param {Array} selections Array of currently selected nodes
12877          */
12878             "beforeselect" : true,
12879         /**
12880          * @event preparedata
12881          * Fires on every row to render, to allow you to change the data.
12882          * @param {Roo.View} this
12883          * @param {Object} data to be rendered (change this)
12884          */
12885           "preparedata" : true
12886           
12887           
12888         });
12889
12890
12891
12892     this.el.on({
12893         "click": this.onClick,
12894         "dblclick": this.onDblClick,
12895         "contextmenu": this.onContextMenu,
12896         scope:this
12897     });
12898
12899     this.selections = [];
12900     this.nodes = [];
12901     this.cmp = new Roo.CompositeElementLite([]);
12902     if(this.store){
12903         this.store = Roo.factory(this.store, Roo.data);
12904         this.setStore(this.store, true);
12905     }
12906     
12907     if ( this.footer && this.footer.xtype) {
12908            
12909          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12910         
12911         this.footer.dataSource = this.store
12912         this.footer.container = fctr;
12913         this.footer = Roo.factory(this.footer, Roo);
12914         fctr.insertFirst(this.el);
12915         
12916         // this is a bit insane - as the paging toolbar seems to detach the el..
12917 //        dom.parentNode.parentNode.parentNode
12918          // they get detached?
12919     }
12920     
12921     
12922     Roo.View.superclass.constructor.call(this);
12923     
12924     
12925 };
12926
12927 Roo.extend(Roo.View, Roo.util.Observable, {
12928     
12929      /**
12930      * @cfg {Roo.data.Store} store Data store to load data from.
12931      */
12932     store : false,
12933     
12934     /**
12935      * @cfg {String|Roo.Element} el The container element.
12936      */
12937     el : '',
12938     
12939     /**
12940      * @cfg {String|Roo.Template} tpl The template used by this View 
12941      */
12942     tpl : false,
12943     /**
12944      * @cfg {String} dataName the named area of the template to use as the data area
12945      *                          Works with domtemplates roo-name="name"
12946      */
12947     dataName: false,
12948     /**
12949      * @cfg {String} selectedClass The css class to add to selected nodes
12950      */
12951     selectedClass : "x-view-selected",
12952      /**
12953      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12954      */
12955     emptyText : "",
12956     
12957     /**
12958      * @cfg {String} text to display on mask (default Loading)
12959      */
12960     mask : false,
12961     /**
12962      * @cfg {Boolean} multiSelect Allow multiple selection
12963      */
12964     multiSelect : false,
12965     /**
12966      * @cfg {Boolean} singleSelect Allow single selection
12967      */
12968     singleSelect:  false,
12969     
12970     /**
12971      * @cfg {Boolean} toggleSelect - selecting 
12972      */
12973     toggleSelect : false,
12974     
12975     /**
12976      * @cfg {Boolean} tickable - selecting 
12977      */
12978     tickable : false,
12979     
12980     /**
12981      * Returns the element this view is bound to.
12982      * @return {Roo.Element}
12983      */
12984     getEl : function(){
12985         return this.wrapEl;
12986     },
12987     
12988     
12989
12990     /**
12991      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12992      */
12993     refresh : function(){
12994         //Roo.log('refresh');
12995         var t = this.tpl;
12996         
12997         // if we are using something like 'domtemplate', then
12998         // the what gets used is:
12999         // t.applySubtemplate(NAME, data, wrapping data..)
13000         // the outer template then get' applied with
13001         //     the store 'extra data'
13002         // and the body get's added to the
13003         //      roo-name="data" node?
13004         //      <span class='roo-tpl-{name}'></span> ?????
13005         
13006         
13007         
13008         this.clearSelections();
13009         this.el.update("");
13010         var html = [];
13011         var records = this.store.getRange();
13012         if(records.length < 1) {
13013             
13014             // is this valid??  = should it render a template??
13015             
13016             this.el.update(this.emptyText);
13017             return;
13018         }
13019         var el = this.el;
13020         if (this.dataName) {
13021             this.el.update(t.apply(this.store.meta)); //????
13022             el = this.el.child('.roo-tpl-' + this.dataName);
13023         }
13024         
13025         for(var i = 0, len = records.length; i < len; i++){
13026             var data = this.prepareData(records[i].data, i, records[i]);
13027             this.fireEvent("preparedata", this, data, i, records[i]);
13028             
13029             var d = Roo.apply({}, data);
13030             
13031             if(this.tickable){
13032                 Roo.apply(d, {'roo-id' : Roo.id()});
13033                 
13034                 var _this = this;
13035             
13036                 Roo.each(this.parent.item, function(item){
13037                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13038                         return;
13039                     }
13040                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13041                 });
13042             }
13043             
13044             html[html.length] = Roo.util.Format.trim(
13045                 this.dataName ?
13046                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13047                     t.apply(d)
13048             );
13049         }
13050         
13051         
13052         
13053         el.update(html.join(""));
13054         this.nodes = el.dom.childNodes;
13055         this.updateIndexes(0);
13056     },
13057     
13058
13059     /**
13060      * Function to override to reformat the data that is sent to
13061      * the template for each node.
13062      * DEPRICATED - use the preparedata event handler.
13063      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13064      * a JSON object for an UpdateManager bound view).
13065      */
13066     prepareData : function(data, index, record)
13067     {
13068         this.fireEvent("preparedata", this, data, index, record);
13069         return data;
13070     },
13071
13072     onUpdate : function(ds, record){
13073         // Roo.log('on update');   
13074         this.clearSelections();
13075         var index = this.store.indexOf(record);
13076         var n = this.nodes[index];
13077         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13078         n.parentNode.removeChild(n);
13079         this.updateIndexes(index, index);
13080     },
13081
13082     
13083     
13084 // --------- FIXME     
13085     onAdd : function(ds, records, index)
13086     {
13087         //Roo.log(['on Add', ds, records, index] );        
13088         this.clearSelections();
13089         if(this.nodes.length == 0){
13090             this.refresh();
13091             return;
13092         }
13093         var n = this.nodes[index];
13094         for(var i = 0, len = records.length; i < len; i++){
13095             var d = this.prepareData(records[i].data, i, records[i]);
13096             if(n){
13097                 this.tpl.insertBefore(n, d);
13098             }else{
13099                 
13100                 this.tpl.append(this.el, d);
13101             }
13102         }
13103         this.updateIndexes(index);
13104     },
13105
13106     onRemove : function(ds, record, index){
13107        // Roo.log('onRemove');
13108         this.clearSelections();
13109         var el = this.dataName  ?
13110             this.el.child('.roo-tpl-' + this.dataName) :
13111             this.el; 
13112         
13113         el.dom.removeChild(this.nodes[index]);
13114         this.updateIndexes(index);
13115     },
13116
13117     /**
13118      * Refresh an individual node.
13119      * @param {Number} index
13120      */
13121     refreshNode : function(index){
13122         this.onUpdate(this.store, this.store.getAt(index));
13123     },
13124
13125     updateIndexes : function(startIndex, endIndex){
13126         var ns = this.nodes;
13127         startIndex = startIndex || 0;
13128         endIndex = endIndex || ns.length - 1;
13129         for(var i = startIndex; i <= endIndex; i++){
13130             ns[i].nodeIndex = i;
13131         }
13132     },
13133
13134     /**
13135      * Changes the data store this view uses and refresh the view.
13136      * @param {Store} store
13137      */
13138     setStore : function(store, initial){
13139         if(!initial && this.store){
13140             this.store.un("datachanged", this.refresh);
13141             this.store.un("add", this.onAdd);
13142             this.store.un("remove", this.onRemove);
13143             this.store.un("update", this.onUpdate);
13144             this.store.un("clear", this.refresh);
13145             this.store.un("beforeload", this.onBeforeLoad);
13146             this.store.un("load", this.onLoad);
13147             this.store.un("loadexception", this.onLoad);
13148         }
13149         if(store){
13150           
13151             store.on("datachanged", this.refresh, this);
13152             store.on("add", this.onAdd, this);
13153             store.on("remove", this.onRemove, this);
13154             store.on("update", this.onUpdate, this);
13155             store.on("clear", this.refresh, this);
13156             store.on("beforeload", this.onBeforeLoad, this);
13157             store.on("load", this.onLoad, this);
13158             store.on("loadexception", this.onLoad, this);
13159         }
13160         
13161         if(store){
13162             this.refresh();
13163         }
13164     },
13165     /**
13166      * onbeforeLoad - masks the loading area.
13167      *
13168      */
13169     onBeforeLoad : function(store,opts)
13170     {
13171          //Roo.log('onBeforeLoad');   
13172         if (!opts.add) {
13173             this.el.update("");
13174         }
13175         this.el.mask(this.mask ? this.mask : "Loading" ); 
13176     },
13177     onLoad : function ()
13178     {
13179         this.el.unmask();
13180     },
13181     
13182
13183     /**
13184      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13185      * @param {HTMLElement} node
13186      * @return {HTMLElement} The template node
13187      */
13188     findItemFromChild : function(node){
13189         var el = this.dataName  ?
13190             this.el.child('.roo-tpl-' + this.dataName,true) :
13191             this.el.dom; 
13192         
13193         if(!node || node.parentNode == el){
13194                     return node;
13195             }
13196             var p = node.parentNode;
13197             while(p && p != el){
13198             if(p.parentNode == el){
13199                 return p;
13200             }
13201             p = p.parentNode;
13202         }
13203             return null;
13204     },
13205
13206     /** @ignore */
13207     onClick : function(e){
13208         var item = this.findItemFromChild(e.getTarget());
13209         if(item){
13210             var index = this.indexOf(item);
13211             if(this.onItemClick(item, index, e) !== false){
13212                 this.fireEvent("click", this, index, item, e);
13213             }
13214         }else{
13215             this.clearSelections();
13216         }
13217     },
13218
13219     /** @ignore */
13220     onContextMenu : function(e){
13221         var item = this.findItemFromChild(e.getTarget());
13222         if(item){
13223             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13224         }
13225     },
13226
13227     /** @ignore */
13228     onDblClick : function(e){
13229         var item = this.findItemFromChild(e.getTarget());
13230         if(item){
13231             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13232         }
13233     },
13234
13235     onItemClick : function(item, index, e)
13236     {
13237         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13238             return false;
13239         }
13240         if (this.toggleSelect) {
13241             var m = this.isSelected(item) ? 'unselect' : 'select';
13242             //Roo.log(m);
13243             var _t = this;
13244             _t[m](item, true, false);
13245             return true;
13246         }
13247         if(this.multiSelect || this.singleSelect){
13248             if(this.multiSelect && e.shiftKey && this.lastSelection){
13249                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13250             }else{
13251                 this.select(item, this.multiSelect && e.ctrlKey);
13252                 this.lastSelection = item;
13253             }
13254             
13255             if(!this.tickable){
13256                 e.preventDefault();
13257             }
13258             
13259         }
13260         return true;
13261     },
13262
13263     /**
13264      * Get the number of selected nodes.
13265      * @return {Number}
13266      */
13267     getSelectionCount : function(){
13268         return this.selections.length;
13269     },
13270
13271     /**
13272      * Get the currently selected nodes.
13273      * @return {Array} An array of HTMLElements
13274      */
13275     getSelectedNodes : function(){
13276         return this.selections;
13277     },
13278
13279     /**
13280      * Get the indexes of the selected nodes.
13281      * @return {Array}
13282      */
13283     getSelectedIndexes : function(){
13284         var indexes = [], s = this.selections;
13285         for(var i = 0, len = s.length; i < len; i++){
13286             indexes.push(s[i].nodeIndex);
13287         }
13288         return indexes;
13289     },
13290
13291     /**
13292      * Clear all selections
13293      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13294      */
13295     clearSelections : function(suppressEvent){
13296         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13297             this.cmp.elements = this.selections;
13298             this.cmp.removeClass(this.selectedClass);
13299             this.selections = [];
13300             if(!suppressEvent){
13301                 this.fireEvent("selectionchange", this, this.selections);
13302             }
13303         }
13304     },
13305
13306     /**
13307      * Returns true if the passed node is selected
13308      * @param {HTMLElement/Number} node The node or node index
13309      * @return {Boolean}
13310      */
13311     isSelected : function(node){
13312         var s = this.selections;
13313         if(s.length < 1){
13314             return false;
13315         }
13316         node = this.getNode(node);
13317         return s.indexOf(node) !== -1;
13318     },
13319
13320     /**
13321      * Selects nodes.
13322      * @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
13323      * @param {Boolean} keepExisting (optional) true to keep existing selections
13324      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13325      */
13326     select : function(nodeInfo, keepExisting, suppressEvent){
13327         if(nodeInfo instanceof Array){
13328             if(!keepExisting){
13329                 this.clearSelections(true);
13330             }
13331             for(var i = 0, len = nodeInfo.length; i < len; i++){
13332                 this.select(nodeInfo[i], true, true);
13333             }
13334             return;
13335         } 
13336         var node = this.getNode(nodeInfo);
13337         if(!node || this.isSelected(node)){
13338             return; // already selected.
13339         }
13340         if(!keepExisting){
13341             this.clearSelections(true);
13342         }
13343         
13344         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13345             Roo.fly(node).addClass(this.selectedClass);
13346             this.selections.push(node);
13347             if(!suppressEvent){
13348                 this.fireEvent("selectionchange", this, this.selections);
13349             }
13350         }
13351         
13352         
13353     },
13354       /**
13355      * Unselects nodes.
13356      * @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
13357      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13358      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13359      */
13360     unselect : function(nodeInfo, keepExisting, suppressEvent)
13361     {
13362         if(nodeInfo instanceof Array){
13363             Roo.each(this.selections, function(s) {
13364                 this.unselect(s, nodeInfo);
13365             }, this);
13366             return;
13367         }
13368         var node = this.getNode(nodeInfo);
13369         if(!node || !this.isSelected(node)){
13370             //Roo.log("not selected");
13371             return; // not selected.
13372         }
13373         // fireevent???
13374         var ns = [];
13375         Roo.each(this.selections, function(s) {
13376             if (s == node ) {
13377                 Roo.fly(node).removeClass(this.selectedClass);
13378
13379                 return;
13380             }
13381             ns.push(s);
13382         },this);
13383         
13384         this.selections= ns;
13385         this.fireEvent("selectionchange", this, this.selections);
13386     },
13387
13388     /**
13389      * Gets a template node.
13390      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13391      * @return {HTMLElement} The node or null if it wasn't found
13392      */
13393     getNode : function(nodeInfo){
13394         if(typeof nodeInfo == "string"){
13395             return document.getElementById(nodeInfo);
13396         }else if(typeof nodeInfo == "number"){
13397             return this.nodes[nodeInfo];
13398         }
13399         return nodeInfo;
13400     },
13401
13402     /**
13403      * Gets a range template nodes.
13404      * @param {Number} startIndex
13405      * @param {Number} endIndex
13406      * @return {Array} An array of nodes
13407      */
13408     getNodes : function(start, end){
13409         var ns = this.nodes;
13410         start = start || 0;
13411         end = typeof end == "undefined" ? ns.length - 1 : end;
13412         var nodes = [];
13413         if(start <= end){
13414             for(var i = start; i <= end; i++){
13415                 nodes.push(ns[i]);
13416             }
13417         } else{
13418             for(var i = start; i >= end; i--){
13419                 nodes.push(ns[i]);
13420             }
13421         }
13422         return nodes;
13423     },
13424
13425     /**
13426      * Finds the index of the passed node
13427      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13428      * @return {Number} The index of the node or -1
13429      */
13430     indexOf : function(node){
13431         node = this.getNode(node);
13432         if(typeof node.nodeIndex == "number"){
13433             return node.nodeIndex;
13434         }
13435         var ns = this.nodes;
13436         for(var i = 0, len = ns.length; i < len; i++){
13437             if(ns[i] == node){
13438                 return i;
13439             }
13440         }
13441         return -1;
13442     }
13443 });
13444 /*
13445  * - LGPL
13446  *
13447  * based on jquery fullcalendar
13448  * 
13449  */
13450
13451 Roo.bootstrap = Roo.bootstrap || {};
13452 /**
13453  * @class Roo.bootstrap.Calendar
13454  * @extends Roo.bootstrap.Component
13455  * Bootstrap Calendar class
13456  * @cfg {Boolean} loadMask (true|false) default false
13457  * @cfg {Object} header generate the user specific header of the calendar, default false
13458
13459  * @constructor
13460  * Create a new Container
13461  * @param {Object} config The config object
13462  */
13463
13464
13465
13466 Roo.bootstrap.Calendar = function(config){
13467     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13468      this.addEvents({
13469         /**
13470              * @event select
13471              * Fires when a date is selected
13472              * @param {DatePicker} this
13473              * @param {Date} date The selected date
13474              */
13475         'select': true,
13476         /**
13477              * @event monthchange
13478              * Fires when the displayed month changes 
13479              * @param {DatePicker} this
13480              * @param {Date} date The selected month
13481              */
13482         'monthchange': true,
13483         /**
13484              * @event evententer
13485              * Fires when mouse over an event
13486              * @param {Calendar} this
13487              * @param {event} Event
13488              */
13489         'evententer': true,
13490         /**
13491              * @event eventleave
13492              * Fires when the mouse leaves an
13493              * @param {Calendar} this
13494              * @param {event}
13495              */
13496         'eventleave': true,
13497         /**
13498              * @event eventclick
13499              * Fires when the mouse click an
13500              * @param {Calendar} this
13501              * @param {event}
13502              */
13503         'eventclick': true
13504         
13505     });
13506
13507 };
13508
13509 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13510     
13511      /**
13512      * @cfg {Number} startDay
13513      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13514      */
13515     startDay : 0,
13516     
13517     loadMask : false,
13518     
13519     header : false,
13520       
13521     getAutoCreate : function(){
13522         
13523         
13524         var fc_button = function(name, corner, style, content ) {
13525             return Roo.apply({},{
13526                 tag : 'span',
13527                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13528                          (corner.length ?
13529                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13530                             ''
13531                         ),
13532                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13533                 unselectable: 'on'
13534             });
13535         };
13536         
13537         var header = {};
13538         
13539         if(!this.header){
13540             header = {
13541                 tag : 'table',
13542                 cls : 'fc-header',
13543                 style : 'width:100%',
13544                 cn : [
13545                     {
13546                         tag: 'tr',
13547                         cn : [
13548                             {
13549                                 tag : 'td',
13550                                 cls : 'fc-header-left',
13551                                 cn : [
13552                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13553                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13554                                     { tag: 'span', cls: 'fc-header-space' },
13555                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13556
13557
13558                                 ]
13559                             },
13560
13561                             {
13562                                 tag : 'td',
13563                                 cls : 'fc-header-center',
13564                                 cn : [
13565                                     {
13566                                         tag: 'span',
13567                                         cls: 'fc-header-title',
13568                                         cn : {
13569                                             tag: 'H2',
13570                                             html : 'month / year'
13571                                         }
13572                                     }
13573
13574                                 ]
13575                             },
13576                             {
13577                                 tag : 'td',
13578                                 cls : 'fc-header-right',
13579                                 cn : [
13580                               /*      fc_button('month', 'left', '', 'month' ),
13581                                     fc_button('week', '', '', 'week' ),
13582                                     fc_button('day', 'right', '', 'day' )
13583                                 */    
13584
13585                                 ]
13586                             }
13587
13588                         ]
13589                     }
13590                 ]
13591             };
13592         }
13593         
13594         header = this.header;
13595         
13596        
13597         var cal_heads = function() {
13598             var ret = [];
13599             // fixme - handle this.
13600             
13601             for (var i =0; i < Date.dayNames.length; i++) {
13602                 var d = Date.dayNames[i];
13603                 ret.push({
13604                     tag: 'th',
13605                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13606                     html : d.substring(0,3)
13607                 });
13608                 
13609             }
13610             ret[0].cls += ' fc-first';
13611             ret[6].cls += ' fc-last';
13612             return ret;
13613         };
13614         var cal_cell = function(n) {
13615             return  {
13616                 tag: 'td',
13617                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13618                 cn : [
13619                     {
13620                         cn : [
13621                             {
13622                                 cls: 'fc-day-number',
13623                                 html: 'D'
13624                             },
13625                             {
13626                                 cls: 'fc-day-content',
13627                              
13628                                 cn : [
13629                                      {
13630                                         style: 'position: relative;' // height: 17px;
13631                                     }
13632                                 ]
13633                             }
13634                             
13635                             
13636                         ]
13637                     }
13638                 ]
13639                 
13640             }
13641         };
13642         var cal_rows = function() {
13643             
13644             var ret = [];
13645             for (var r = 0; r < 6; r++) {
13646                 var row= {
13647                     tag : 'tr',
13648                     cls : 'fc-week',
13649                     cn : []
13650                 };
13651                 
13652                 for (var i =0; i < Date.dayNames.length; i++) {
13653                     var d = Date.dayNames[i];
13654                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13655
13656                 }
13657                 row.cn[0].cls+=' fc-first';
13658                 row.cn[0].cn[0].style = 'min-height:90px';
13659                 row.cn[6].cls+=' fc-last';
13660                 ret.push(row);
13661                 
13662             }
13663             ret[0].cls += ' fc-first';
13664             ret[4].cls += ' fc-prev-last';
13665             ret[5].cls += ' fc-last';
13666             return ret;
13667             
13668         };
13669         
13670         var cal_table = {
13671             tag: 'table',
13672             cls: 'fc-border-separate',
13673             style : 'width:100%',
13674             cellspacing  : 0,
13675             cn : [
13676                 { 
13677                     tag: 'thead',
13678                     cn : [
13679                         { 
13680                             tag: 'tr',
13681                             cls : 'fc-first fc-last',
13682                             cn : cal_heads()
13683                         }
13684                     ]
13685                 },
13686                 { 
13687                     tag: 'tbody',
13688                     cn : cal_rows()
13689                 }
13690                   
13691             ]
13692         };
13693          
13694          var cfg = {
13695             cls : 'fc fc-ltr',
13696             cn : [
13697                 header,
13698                 {
13699                     cls : 'fc-content',
13700                     style : "position: relative;",
13701                     cn : [
13702                         {
13703                             cls : 'fc-view fc-view-month fc-grid',
13704                             style : 'position: relative',
13705                             unselectable : 'on',
13706                             cn : [
13707                                 {
13708                                     cls : 'fc-event-container',
13709                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13710                                 },
13711                                 cal_table
13712                             ]
13713                         }
13714                     ]
13715     
13716                 }
13717            ] 
13718             
13719         };
13720         
13721          
13722         
13723         return cfg;
13724     },
13725     
13726     
13727     initEvents : function()
13728     {
13729         if(!this.store){
13730             throw "can not find store for calendar";
13731         }
13732         
13733         var mark = {
13734             tag: "div",
13735             cls:"x-dlg-mask",
13736             style: "text-align:center",
13737             cn: [
13738                 {
13739                     tag: "div",
13740                     style: "background-color:white;width:50%;margin:250 auto",
13741                     cn: [
13742                         {
13743                             tag: "img",
13744                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13745                         },
13746                         {
13747                             tag: "span",
13748                             html: "Loading"
13749                         }
13750                         
13751                     ]
13752                 }
13753             ]
13754         }
13755         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13756         
13757         var size = this.el.select('.fc-content', true).first().getSize();
13758         this.maskEl.setSize(size.width, size.height);
13759         this.maskEl.enableDisplayMode("block");
13760         if(!this.loadMask){
13761             this.maskEl.hide();
13762         }
13763         
13764         this.store = Roo.factory(this.store, Roo.data);
13765         this.store.on('load', this.onLoad, this);
13766         this.store.on('beforeload', this.onBeforeLoad, this);
13767         
13768         this.resize();
13769         
13770         this.cells = this.el.select('.fc-day',true);
13771         //Roo.log(this.cells);
13772         this.textNodes = this.el.query('.fc-day-number');
13773         this.cells.addClassOnOver('fc-state-hover');
13774         
13775         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13776         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13777         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13778         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13779         
13780         this.on('monthchange', this.onMonthChange, this);
13781         
13782         this.update(new Date().clearTime());
13783     },
13784     
13785     resize : function() {
13786         var sz  = this.el.getSize();
13787         
13788         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13789         this.el.select('.fc-day-content div',true).setHeight(34);
13790     },
13791     
13792     
13793     // private
13794     showPrevMonth : function(e){
13795         this.update(this.activeDate.add("mo", -1));
13796     },
13797     showToday : function(e){
13798         this.update(new Date().clearTime());
13799     },
13800     // private
13801     showNextMonth : function(e){
13802         this.update(this.activeDate.add("mo", 1));
13803     },
13804
13805     // private
13806     showPrevYear : function(){
13807         this.update(this.activeDate.add("y", -1));
13808     },
13809
13810     // private
13811     showNextYear : function(){
13812         this.update(this.activeDate.add("y", 1));
13813     },
13814
13815     
13816    // private
13817     update : function(date)
13818     {
13819         var vd = this.activeDate;
13820         this.activeDate = date;
13821 //        if(vd && this.el){
13822 //            var t = date.getTime();
13823 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13824 //                Roo.log('using add remove');
13825 //                
13826 //                this.fireEvent('monthchange', this, date);
13827 //                
13828 //                this.cells.removeClass("fc-state-highlight");
13829 //                this.cells.each(function(c){
13830 //                   if(c.dateValue == t){
13831 //                       c.addClass("fc-state-highlight");
13832 //                       setTimeout(function(){
13833 //                            try{c.dom.firstChild.focus();}catch(e){}
13834 //                       }, 50);
13835 //                       return false;
13836 //                   }
13837 //                   return true;
13838 //                });
13839 //                return;
13840 //            }
13841 //        }
13842         
13843         var days = date.getDaysInMonth();
13844         
13845         var firstOfMonth = date.getFirstDateOfMonth();
13846         var startingPos = firstOfMonth.getDay()-this.startDay;
13847         
13848         if(startingPos < this.startDay){
13849             startingPos += 7;
13850         }
13851         
13852         var pm = date.add(Date.MONTH, -1);
13853         var prevStart = pm.getDaysInMonth()-startingPos;
13854 //        
13855         this.cells = this.el.select('.fc-day',true);
13856         this.textNodes = this.el.query('.fc-day-number');
13857         this.cells.addClassOnOver('fc-state-hover');
13858         
13859         var cells = this.cells.elements;
13860         var textEls = this.textNodes;
13861         
13862         Roo.each(cells, function(cell){
13863             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13864         });
13865         
13866         days += startingPos;
13867
13868         // convert everything to numbers so it's fast
13869         var day = 86400000;
13870         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13871         //Roo.log(d);
13872         //Roo.log(pm);
13873         //Roo.log(prevStart);
13874         
13875         var today = new Date().clearTime().getTime();
13876         var sel = date.clearTime().getTime();
13877         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13878         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13879         var ddMatch = this.disabledDatesRE;
13880         var ddText = this.disabledDatesText;
13881         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13882         var ddaysText = this.disabledDaysText;
13883         var format = this.format;
13884         
13885         var setCellClass = function(cal, cell){
13886             cell.row = 0;
13887             cell.events = [];
13888             cell.more = [];
13889             //Roo.log('set Cell Class');
13890             cell.title = "";
13891             var t = d.getTime();
13892             
13893             //Roo.log(d);
13894             
13895             cell.dateValue = t;
13896             if(t == today){
13897                 cell.className += " fc-today";
13898                 cell.className += " fc-state-highlight";
13899                 cell.title = cal.todayText;
13900             }
13901             if(t == sel){
13902                 // disable highlight in other month..
13903                 //cell.className += " fc-state-highlight";
13904                 
13905             }
13906             // disabling
13907             if(t < min) {
13908                 cell.className = " fc-state-disabled";
13909                 cell.title = cal.minText;
13910                 return;
13911             }
13912             if(t > max) {
13913                 cell.className = " fc-state-disabled";
13914                 cell.title = cal.maxText;
13915                 return;
13916             }
13917             if(ddays){
13918                 if(ddays.indexOf(d.getDay()) != -1){
13919                     cell.title = ddaysText;
13920                     cell.className = " fc-state-disabled";
13921                 }
13922             }
13923             if(ddMatch && format){
13924                 var fvalue = d.dateFormat(format);
13925                 if(ddMatch.test(fvalue)){
13926                     cell.title = ddText.replace("%0", fvalue);
13927                     cell.className = " fc-state-disabled";
13928                 }
13929             }
13930             
13931             if (!cell.initialClassName) {
13932                 cell.initialClassName = cell.dom.className;
13933             }
13934             
13935             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13936         };
13937
13938         var i = 0;
13939         
13940         for(; i < startingPos; i++) {
13941             textEls[i].innerHTML = (++prevStart);
13942             d.setDate(d.getDate()+1);
13943             
13944             cells[i].className = "fc-past fc-other-month";
13945             setCellClass(this, cells[i]);
13946         }
13947         
13948         var intDay = 0;
13949         
13950         for(; i < days; i++){
13951             intDay = i - startingPos + 1;
13952             textEls[i].innerHTML = (intDay);
13953             d.setDate(d.getDate()+1);
13954             
13955             cells[i].className = ''; // "x-date-active";
13956             setCellClass(this, cells[i]);
13957         }
13958         var extraDays = 0;
13959         
13960         for(; i < 42; i++) {
13961             textEls[i].innerHTML = (++extraDays);
13962             d.setDate(d.getDate()+1);
13963             
13964             cells[i].className = "fc-future fc-other-month";
13965             setCellClass(this, cells[i]);
13966         }
13967         
13968         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13969         
13970         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13971         
13972         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13973         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13974         
13975         if(totalRows != 6){
13976             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13977             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13978         }
13979         
13980         this.fireEvent('monthchange', this, date);
13981         
13982         
13983         /*
13984         if(!this.internalRender){
13985             var main = this.el.dom.firstChild;
13986             var w = main.offsetWidth;
13987             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13988             Roo.fly(main).setWidth(w);
13989             this.internalRender = true;
13990             // opera does not respect the auto grow header center column
13991             // then, after it gets a width opera refuses to recalculate
13992             // without a second pass
13993             if(Roo.isOpera && !this.secondPass){
13994                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13995                 this.secondPass = true;
13996                 this.update.defer(10, this, [date]);
13997             }
13998         }
13999         */
14000         
14001     },
14002     
14003     findCell : function(dt) {
14004         dt = dt.clearTime().getTime();
14005         var ret = false;
14006         this.cells.each(function(c){
14007             //Roo.log("check " +c.dateValue + '?=' + dt);
14008             if(c.dateValue == dt){
14009                 ret = c;
14010                 return false;
14011             }
14012             return true;
14013         });
14014         
14015         return ret;
14016     },
14017     
14018     findCells : function(ev) {
14019         var s = ev.start.clone().clearTime().getTime();
14020        // Roo.log(s);
14021         var e= ev.end.clone().clearTime().getTime();
14022        // Roo.log(e);
14023         var ret = [];
14024         this.cells.each(function(c){
14025              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14026             
14027             if(c.dateValue > e){
14028                 return ;
14029             }
14030             if(c.dateValue < s){
14031                 return ;
14032             }
14033             ret.push(c);
14034         });
14035         
14036         return ret;    
14037     },
14038     
14039 //    findBestRow: function(cells)
14040 //    {
14041 //        var ret = 0;
14042 //        
14043 //        for (var i =0 ; i < cells.length;i++) {
14044 //            ret  = Math.max(cells[i].rows || 0,ret);
14045 //        }
14046 //        return ret;
14047 //        
14048 //    },
14049     
14050     
14051     addItem : function(ev)
14052     {
14053         // look for vertical location slot in
14054         var cells = this.findCells(ev);
14055         
14056 //        ev.row = this.findBestRow(cells);
14057         
14058         // work out the location.
14059         
14060         var crow = false;
14061         var rows = [];
14062         for(var i =0; i < cells.length; i++) {
14063             
14064             cells[i].row = cells[0].row;
14065             
14066             if(i == 0){
14067                 cells[i].row = cells[i].row + 1;
14068             }
14069             
14070             if (!crow) {
14071                 crow = {
14072                     start : cells[i],
14073                     end :  cells[i]
14074                 };
14075                 continue;
14076             }
14077             if (crow.start.getY() == cells[i].getY()) {
14078                 // on same row.
14079                 crow.end = cells[i];
14080                 continue;
14081             }
14082             // different row.
14083             rows.push(crow);
14084             crow = {
14085                 start: cells[i],
14086                 end : cells[i]
14087             };
14088             
14089         }
14090         
14091         rows.push(crow);
14092         ev.els = [];
14093         ev.rows = rows;
14094         ev.cells = cells;
14095         
14096         cells[0].events.push(ev);
14097         
14098         this.calevents.push(ev);
14099     },
14100     
14101     clearEvents: function() {
14102         
14103         if(!this.calevents){
14104             return;
14105         }
14106         
14107         Roo.each(this.cells.elements, function(c){
14108             c.row = 0;
14109             c.events = [];
14110             c.more = [];
14111         });
14112         
14113         Roo.each(this.calevents, function(e) {
14114             Roo.each(e.els, function(el) {
14115                 el.un('mouseenter' ,this.onEventEnter, this);
14116                 el.un('mouseleave' ,this.onEventLeave, this);
14117                 el.remove();
14118             },this);
14119         },this);
14120         
14121         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14122             e.remove();
14123         });
14124         
14125     },
14126     
14127     renderEvents: function()
14128     {   
14129         var _this = this;
14130         
14131         this.cells.each(function(c) {
14132             
14133             if(c.row < 5){
14134                 return;
14135             }
14136             
14137             var ev = c.events;
14138             
14139             var r = 4;
14140             if(c.row != c.events.length){
14141                 r = 4 - (4 - (c.row - c.events.length));
14142             }
14143             
14144             c.events = ev.slice(0, r);
14145             c.more = ev.slice(r);
14146             
14147             if(c.more.length && c.more.length == 1){
14148                 c.events.push(c.more.pop());
14149             }
14150             
14151             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14152             
14153         });
14154             
14155         this.cells.each(function(c) {
14156             
14157             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14158             
14159             
14160             for (var e = 0; e < c.events.length; e++){
14161                 var ev = c.events[e];
14162                 var rows = ev.rows;
14163                 
14164                 for(var i = 0; i < rows.length; i++) {
14165                 
14166                     // how many rows should it span..
14167
14168                     var  cfg = {
14169                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14170                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14171
14172                         unselectable : "on",
14173                         cn : [
14174                             {
14175                                 cls: 'fc-event-inner',
14176                                 cn : [
14177     //                                {
14178     //                                  tag:'span',
14179     //                                  cls: 'fc-event-time',
14180     //                                  html : cells.length > 1 ? '' : ev.time
14181     //                                },
14182                                     {
14183                                       tag:'span',
14184                                       cls: 'fc-event-title',
14185                                       html : String.format('{0}', ev.title)
14186                                     }
14187
14188
14189                                 ]
14190                             },
14191                             {
14192                                 cls: 'ui-resizable-handle ui-resizable-e',
14193                                 html : '&nbsp;&nbsp;&nbsp'
14194                             }
14195
14196                         ]
14197                     };
14198
14199                     if (i == 0) {
14200                         cfg.cls += ' fc-event-start';
14201                     }
14202                     if ((i+1) == rows.length) {
14203                         cfg.cls += ' fc-event-end';
14204                     }
14205
14206                     var ctr = _this.el.select('.fc-event-container',true).first();
14207                     var cg = ctr.createChild(cfg);
14208
14209                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14210                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14211
14212                     var r = (c.more.length) ? 1 : 0;
14213                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14214                     cg.setWidth(ebox.right - sbox.x -2);
14215
14216                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14217                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14218                     cg.on('click', _this.onEventClick, _this, ev);
14219
14220                     ev.els.push(cg);
14221                     
14222                 }
14223                 
14224             }
14225             
14226             
14227             if(c.more.length){
14228                 var  cfg = {
14229                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14230                     style : 'position: absolute',
14231                     unselectable : "on",
14232                     cn : [
14233                         {
14234                             cls: 'fc-event-inner',
14235                             cn : [
14236                                 {
14237                                   tag:'span',
14238                                   cls: 'fc-event-title',
14239                                   html : 'More'
14240                                 }
14241
14242
14243                             ]
14244                         },
14245                         {
14246                             cls: 'ui-resizable-handle ui-resizable-e',
14247                             html : '&nbsp;&nbsp;&nbsp'
14248                         }
14249
14250                     ]
14251                 };
14252
14253                 var ctr = _this.el.select('.fc-event-container',true).first();
14254                 var cg = ctr.createChild(cfg);
14255
14256                 var sbox = c.select('.fc-day-content',true).first().getBox();
14257                 var ebox = c.select('.fc-day-content',true).first().getBox();
14258                 //Roo.log(cg);
14259                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14260                 cg.setWidth(ebox.right - sbox.x -2);
14261
14262                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14263                 
14264             }
14265             
14266         });
14267         
14268         
14269         
14270     },
14271     
14272     onEventEnter: function (e, el,event,d) {
14273         this.fireEvent('evententer', this, el, event);
14274     },
14275     
14276     onEventLeave: function (e, el,event,d) {
14277         this.fireEvent('eventleave', this, el, event);
14278     },
14279     
14280     onEventClick: function (e, el,event,d) {
14281         this.fireEvent('eventclick', this, el, event);
14282     },
14283     
14284     onMonthChange: function () {
14285         this.store.load();
14286     },
14287     
14288     onMoreEventClick: function(e, el, more)
14289     {
14290         var _this = this;
14291         
14292         this.calpopover.placement = 'right';
14293         this.calpopover.setTitle('More');
14294         
14295         this.calpopover.setContent('');
14296         
14297         var ctr = this.calpopover.el.select('.popover-content', true).first();
14298         
14299         Roo.each(more, function(m){
14300             var cfg = {
14301                 cls : 'fc-event-hori fc-event-draggable',
14302                 html : m.title
14303             }
14304             var cg = ctr.createChild(cfg);
14305             
14306             cg.on('click', _this.onEventClick, _this, m);
14307         });
14308         
14309         this.calpopover.show(el);
14310         
14311         
14312     },
14313     
14314     onLoad: function () 
14315     {   
14316         this.calevents = [];
14317         var cal = this;
14318         
14319         if(this.store.getCount() > 0){
14320             this.store.data.each(function(d){
14321                cal.addItem({
14322                     id : d.data.id,
14323                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14324                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14325                     time : d.data.start_time,
14326                     title : d.data.title,
14327                     description : d.data.description,
14328                     venue : d.data.venue
14329                 });
14330             });
14331         }
14332         
14333         this.renderEvents();
14334         
14335         if(this.calevents.length && this.loadMask){
14336             this.maskEl.hide();
14337         }
14338     },
14339     
14340     onBeforeLoad: function()
14341     {
14342         this.clearEvents();
14343         if(this.loadMask){
14344             this.maskEl.show();
14345         }
14346     }
14347 });
14348
14349  
14350  /*
14351  * - LGPL
14352  *
14353  * element
14354  * 
14355  */
14356
14357 /**
14358  * @class Roo.bootstrap.Popover
14359  * @extends Roo.bootstrap.Component
14360  * Bootstrap Popover class
14361  * @cfg {String} html contents of the popover   (or false to use children..)
14362  * @cfg {String} title of popover (or false to hide)
14363  * @cfg {String} placement how it is placed
14364  * @cfg {String} trigger click || hover (or false to trigger manually)
14365  * @cfg {String} over what (parent or false to trigger manually.)
14366  * @cfg {Number} delay - delay before showing
14367  
14368  * @constructor
14369  * Create a new Popover
14370  * @param {Object} config The config object
14371  */
14372
14373 Roo.bootstrap.Popover = function(config){
14374     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14375 };
14376
14377 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14378     
14379     title: 'Fill in a title',
14380     html: false,
14381     
14382     placement : 'right',
14383     trigger : 'hover', // hover
14384     
14385     delay : 0,
14386     
14387     over: 'parent',
14388     
14389     can_build_overlaid : false,
14390     
14391     getChildContainer : function()
14392     {
14393         return this.el.select('.popover-content',true).first();
14394     },
14395     
14396     getAutoCreate : function(){
14397          Roo.log('make popover?');
14398         var cfg = {
14399            cls : 'popover roo-dynamic',
14400            style: 'display:block',
14401            cn : [
14402                 {
14403                     cls : 'arrow'
14404                 },
14405                 {
14406                     cls : 'popover-inner',
14407                     cn : [
14408                         {
14409                             tag: 'h3',
14410                             cls: 'popover-title',
14411                             html : this.title
14412                         },
14413                         {
14414                             cls : 'popover-content',
14415                             html : this.html
14416                         }
14417                     ]
14418                     
14419                 }
14420            ]
14421         };
14422         
14423         return cfg;
14424     },
14425     setTitle: function(str)
14426     {
14427         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14428     },
14429     setContent: function(str)
14430     {
14431         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14432     },
14433     // as it get's added to the bottom of the page.
14434     onRender : function(ct, position)
14435     {
14436         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14437         if(!this.el){
14438             var cfg = Roo.apply({},  this.getAutoCreate());
14439             cfg.id = Roo.id();
14440             
14441             if (this.cls) {
14442                 cfg.cls += ' ' + this.cls;
14443             }
14444             if (this.style) {
14445                 cfg.style = this.style;
14446             }
14447             Roo.log("adding to ")
14448             this.el = Roo.get(document.body).createChild(cfg, position);
14449             Roo.log(this.el);
14450         }
14451         this.initEvents();
14452     },
14453     
14454     initEvents : function()
14455     {
14456         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14457         this.el.enableDisplayMode('block');
14458         this.el.hide();
14459         if (this.over === false) {
14460             return; 
14461         }
14462         if (this.triggers === false) {
14463             return;
14464         }
14465         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14466         var triggers = this.trigger ? this.trigger.split(' ') : [];
14467         Roo.each(triggers, function(trigger) {
14468         
14469             if (trigger == 'click') {
14470                 on_el.on('click', this.toggle, this);
14471             } else if (trigger != 'manual') {
14472                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14473                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14474       
14475                 on_el.on(eventIn  ,this.enter, this);
14476                 on_el.on(eventOut, this.leave, this);
14477             }
14478         }, this);
14479         
14480     },
14481     
14482     
14483     // private
14484     timeout : null,
14485     hoverState : null,
14486     
14487     toggle : function () {
14488         this.hoverState == 'in' ? this.leave() : this.enter();
14489     },
14490     
14491     enter : function () {
14492        
14493     
14494         clearTimeout(this.timeout);
14495     
14496         this.hoverState = 'in';
14497     
14498         if (!this.delay || !this.delay.show) {
14499             this.show();
14500             return;
14501         }
14502         var _t = this;
14503         this.timeout = setTimeout(function () {
14504             if (_t.hoverState == 'in') {
14505                 _t.show();
14506             }
14507         }, this.delay.show)
14508     },
14509     leave : function() {
14510         clearTimeout(this.timeout);
14511     
14512         this.hoverState = 'out';
14513     
14514         if (!this.delay || !this.delay.hide) {
14515             this.hide();
14516             return;
14517         }
14518         var _t = this;
14519         this.timeout = setTimeout(function () {
14520             if (_t.hoverState == 'out') {
14521                 _t.hide();
14522             }
14523         }, this.delay.hide)
14524     },
14525     
14526     show : function (on_el)
14527     {
14528         if (!on_el) {
14529             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14530         }
14531         // set content.
14532         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14533         if (this.html !== false) {
14534             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14535         }
14536         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14537         if (!this.title.length) {
14538             this.el.select('.popover-title',true).hide();
14539         }
14540         
14541         var placement = typeof this.placement == 'function' ?
14542             this.placement.call(this, this.el, on_el) :
14543             this.placement;
14544             
14545         var autoToken = /\s?auto?\s?/i;
14546         var autoPlace = autoToken.test(placement);
14547         if (autoPlace) {
14548             placement = placement.replace(autoToken, '') || 'top';
14549         }
14550         
14551         //this.el.detach()
14552         //this.el.setXY([0,0]);
14553         this.el.show();
14554         this.el.dom.style.display='block';
14555         this.el.addClass(placement);
14556         
14557         //this.el.appendTo(on_el);
14558         
14559         var p = this.getPosition();
14560         var box = this.el.getBox();
14561         
14562         if (autoPlace) {
14563             // fixme..
14564         }
14565         var align = Roo.bootstrap.Popover.alignment[placement];
14566         this.el.alignTo(on_el, align[0],align[1]);
14567         //var arrow = this.el.select('.arrow',true).first();
14568         //arrow.set(align[2], 
14569         
14570         this.el.addClass('in');
14571         this.hoverState = null;
14572         
14573         if (this.el.hasClass('fade')) {
14574             // fade it?
14575         }
14576         
14577     },
14578     hide : function()
14579     {
14580         this.el.setXY([0,0]);
14581         this.el.removeClass('in');
14582         this.el.hide();
14583         
14584     }
14585     
14586 });
14587
14588 Roo.bootstrap.Popover.alignment = {
14589     'left' : ['r-l', [-10,0], 'right'],
14590     'right' : ['l-r', [10,0], 'left'],
14591     'bottom' : ['t-b', [0,10], 'top'],
14592     'top' : [ 'b-t', [0,-10], 'bottom']
14593 };
14594
14595  /*
14596  * - LGPL
14597  *
14598  * Progress
14599  * 
14600  */
14601
14602 /**
14603  * @class Roo.bootstrap.Progress
14604  * @extends Roo.bootstrap.Component
14605  * Bootstrap Progress class
14606  * @cfg {Boolean} striped striped of the progress bar
14607  * @cfg {Boolean} active animated of the progress bar
14608  * 
14609  * 
14610  * @constructor
14611  * Create a new Progress
14612  * @param {Object} config The config object
14613  */
14614
14615 Roo.bootstrap.Progress = function(config){
14616     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14617 };
14618
14619 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14620     
14621     striped : false,
14622     active: false,
14623     
14624     getAutoCreate : function(){
14625         var cfg = {
14626             tag: 'div',
14627             cls: 'progress'
14628         };
14629         
14630         
14631         if(this.striped){
14632             cfg.cls += ' progress-striped';
14633         }
14634       
14635         if(this.active){
14636             cfg.cls += ' active';
14637         }
14638         
14639         
14640         return cfg;
14641     }
14642    
14643 });
14644
14645  
14646
14647  /*
14648  * - LGPL
14649  *
14650  * ProgressBar
14651  * 
14652  */
14653
14654 /**
14655  * @class Roo.bootstrap.ProgressBar
14656  * @extends Roo.bootstrap.Component
14657  * Bootstrap ProgressBar class
14658  * @cfg {Number} aria_valuenow aria-value now
14659  * @cfg {Number} aria_valuemin aria-value min
14660  * @cfg {Number} aria_valuemax aria-value max
14661  * @cfg {String} label label for the progress bar
14662  * @cfg {String} panel (success | info | warning | danger )
14663  * @cfg {String} role role of the progress bar
14664  * @cfg {String} sr_only text
14665  * 
14666  * 
14667  * @constructor
14668  * Create a new ProgressBar
14669  * @param {Object} config The config object
14670  */
14671
14672 Roo.bootstrap.ProgressBar = function(config){
14673     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14674 };
14675
14676 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14677     
14678     aria_valuenow : 0,
14679     aria_valuemin : 0,
14680     aria_valuemax : 100,
14681     label : false,
14682     panel : false,
14683     role : false,
14684     sr_only: false,
14685     
14686     getAutoCreate : function()
14687     {
14688         
14689         var cfg = {
14690             tag: 'div',
14691             cls: 'progress-bar',
14692             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14693         };
14694         
14695         if(this.sr_only){
14696             cfg.cn = {
14697                 tag: 'span',
14698                 cls: 'sr-only',
14699                 html: this.sr_only
14700             }
14701         }
14702         
14703         if(this.role){
14704             cfg.role = this.role;
14705         }
14706         
14707         if(this.aria_valuenow){
14708             cfg['aria-valuenow'] = this.aria_valuenow;
14709         }
14710         
14711         if(this.aria_valuemin){
14712             cfg['aria-valuemin'] = this.aria_valuemin;
14713         }
14714         
14715         if(this.aria_valuemax){
14716             cfg['aria-valuemax'] = this.aria_valuemax;
14717         }
14718         
14719         if(this.label && !this.sr_only){
14720             cfg.html = this.label;
14721         }
14722         
14723         if(this.panel){
14724             cfg.cls += ' progress-bar-' + this.panel;
14725         }
14726         
14727         return cfg;
14728     },
14729     
14730     update : function(aria_valuenow)
14731     {
14732         this.aria_valuenow = aria_valuenow;
14733         
14734         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14735     }
14736    
14737 });
14738
14739  
14740
14741  /*
14742  * - LGPL
14743  *
14744  * column
14745  * 
14746  */
14747
14748 /**
14749  * @class Roo.bootstrap.TabGroup
14750  * @extends Roo.bootstrap.Column
14751  * Bootstrap Column class
14752  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14753  * @cfg {Boolean} carousel true to make the group behave like a carousel
14754  * @cfg {Number} bullets show the panel pointer.. default 0
14755  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14756  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14757  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14758  * 
14759  * @constructor
14760  * Create a new TabGroup
14761  * @param {Object} config The config object
14762  */
14763
14764 Roo.bootstrap.TabGroup = function(config){
14765     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14766     if (!this.navId) {
14767         this.navId = Roo.id();
14768     }
14769     this.tabs = [];
14770     Roo.bootstrap.TabGroup.register(this);
14771     
14772 };
14773
14774 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14775     
14776     carousel : false,
14777     transition : false,
14778     bullets : 0,
14779     timer : 0,
14780     autoslide : false,
14781     slideFn : false,
14782     slideOnTouch : false,
14783     
14784     getAutoCreate : function()
14785     {
14786         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14787         
14788         cfg.cls += ' tab-content';
14789         
14790         Roo.log('get auto create...............');
14791         
14792         if (this.carousel) {
14793             cfg.cls += ' carousel slide';
14794             
14795             cfg.cn = [{
14796                cls : 'carousel-inner'
14797             }];
14798         
14799             if(this.bullets > 0 && !Roo.isTouch){
14800                 
14801                 var bullets = {
14802                     cls : 'carousel-bullets',
14803                     cn : []
14804                 };
14805                 
14806                 if(this.bullets_cls){
14807                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14808                 }
14809                 
14810                 for (var i = 0; i < this.bullets; i++){
14811                     bullets.cn.push({
14812                         cls : 'bullet bullet-' + i
14813                     });
14814                 }
14815                 
14816                 bullets.cn.push({
14817                     cls : 'clear'
14818                 });
14819                 
14820                 cfg.cn[0].cn = bullets;
14821             }
14822         }
14823         
14824         return cfg;
14825     },
14826     
14827     initEvents:  function()
14828     {
14829         Roo.log('-------- init events on tab group ---------');
14830         
14831         if(this.bullets > 0 && !Roo.isTouch){
14832             this.initBullet();
14833         }
14834         
14835         Roo.log(this);
14836         
14837         if(Roo.isTouch && this.slideOnTouch){
14838             this.el.on("touchstart", this.onTouchStart, this);
14839         }
14840         
14841         if(this.autoslide){
14842             var _this = this;
14843             
14844             this.slideFn = window.setInterval(function() {
14845                 _this.showPanelNext();
14846             }, this.timer);
14847         }
14848         
14849     },
14850     
14851     onTouchStart : function(e, el, o)
14852     {
14853         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14854             return;
14855         }
14856         
14857         this.showPanelNext();
14858     },
14859     
14860     getChildContainer : function()
14861     {
14862         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14863     },
14864     
14865     /**
14866     * register a Navigation item
14867     * @param {Roo.bootstrap.NavItem} the navitem to add
14868     */
14869     register : function(item)
14870     {
14871         this.tabs.push( item);
14872         item.navId = this.navId; // not really needed..
14873     
14874     },
14875     
14876     getActivePanel : function()
14877     {
14878         var r = false;
14879         Roo.each(this.tabs, function(t) {
14880             if (t.active) {
14881                 r = t;
14882                 return false;
14883             }
14884             return null;
14885         });
14886         return r;
14887         
14888     },
14889     getPanelByName : function(n)
14890     {
14891         var r = false;
14892         Roo.each(this.tabs, function(t) {
14893             if (t.tabId == n) {
14894                 r = t;
14895                 return false;
14896             }
14897             return null;
14898         });
14899         return r;
14900     },
14901     indexOfPanel : function(p)
14902     {
14903         var r = false;
14904         Roo.each(this.tabs, function(t,i) {
14905             if (t.tabId == p.tabId) {
14906                 r = i;
14907                 return false;
14908             }
14909             return null;
14910         });
14911         return r;
14912     },
14913     /**
14914      * show a specific panel
14915      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14916      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14917      */
14918     showPanel : function (pan)
14919     {
14920         if(this.transition){
14921             Roo.log("waiting for the transitionend");
14922             return;
14923         }
14924         
14925         if (typeof(pan) == 'number') {
14926             pan = this.tabs[pan];
14927         }
14928         if (typeof(pan) == 'string') {
14929             pan = this.getPanelByName(pan);
14930         }
14931         if (pan.tabId == this.getActivePanel().tabId) {
14932             return true;
14933         }
14934         var cur = this.getActivePanel();
14935         
14936         if (false === cur.fireEvent('beforedeactivate')) {
14937             return false;
14938         }
14939         
14940         if(this.bullets > 0 && !Roo.isTouch){
14941             this.setActiveBullet(this.indexOfPanel(pan));
14942         }
14943         
14944         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14945             
14946             this.transition = true;
14947             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14948             var lr = dir == 'next' ? 'left' : 'right';
14949             pan.el.addClass(dir); // or prev
14950             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14951             cur.el.addClass(lr); // or right
14952             pan.el.addClass(lr);
14953             
14954             var _this = this;
14955             cur.el.on('transitionend', function() {
14956                 Roo.log("trans end?");
14957                 
14958                 pan.el.removeClass([lr,dir]);
14959                 pan.setActive(true);
14960                 
14961                 cur.el.removeClass([lr]);
14962                 cur.setActive(false);
14963                 
14964                 _this.transition = false;
14965                 
14966             }, this, { single:  true } );
14967             
14968             return true;
14969         }
14970         
14971         cur.setActive(false);
14972         pan.setActive(true);
14973         
14974         return true;
14975         
14976     },
14977     showPanelNext : function()
14978     {
14979         var i = this.indexOfPanel(this.getActivePanel());
14980         
14981         if (i >= this.tabs.length - 1 && !this.autoslide) {
14982             return;
14983         }
14984         
14985         if (i >= this.tabs.length - 1 && this.autoslide) {
14986             i = -1;
14987         }
14988         
14989         this.showPanel(this.tabs[i+1]);
14990     },
14991     
14992     showPanelPrev : function()
14993     {
14994         var i = this.indexOfPanel(this.getActivePanel());
14995         
14996         if (i  < 1 && !this.autoslide) {
14997             return;
14998         }
14999         
15000         if (i < 1 && this.autoslide) {
15001             i = this.tabs.length;
15002         }
15003         
15004         this.showPanel(this.tabs[i-1]);
15005     },
15006     
15007     initBullet : function()
15008     {
15009         if(Roo.isTouch){
15010             return;
15011         }
15012         
15013         var _this = this;
15014         
15015         for (var i = 0; i < this.bullets; i++){
15016             var bullet = this.el.select('.bullet-' + i, true).first();
15017
15018             if(!bullet){
15019                 continue;
15020             }
15021
15022             bullet.on('click', (function(e, el, o, ii, t){
15023
15024                 e.preventDefault();
15025
15026                 _this.showPanel(ii);
15027
15028                 if(_this.autoslide && _this.slideFn){
15029                     clearInterval(_this.slideFn);
15030                     _this.slideFn = window.setInterval(function() {
15031                         _this.showPanelNext();
15032                     }, _this.timer);
15033                 }
15034
15035             }).createDelegate(this, [i, bullet], true));
15036         }
15037     },
15038     
15039     setActiveBullet : function(i)
15040     {
15041         if(Roo.isTouch){
15042             return;
15043         }
15044         
15045         Roo.each(this.el.select('.bullet', true).elements, function(el){
15046             el.removeClass('selected');
15047         });
15048
15049         var bullet = this.el.select('.bullet-' + i, true).first();
15050         
15051         if(!bullet){
15052             return;
15053         }
15054         
15055         bullet.addClass('selected');
15056     }
15057     
15058     
15059   
15060 });
15061
15062  
15063
15064  
15065  
15066 Roo.apply(Roo.bootstrap.TabGroup, {
15067     
15068     groups: {},
15069      /**
15070     * register a Navigation Group
15071     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15072     */
15073     register : function(navgrp)
15074     {
15075         this.groups[navgrp.navId] = navgrp;
15076         
15077     },
15078     /**
15079     * fetch a Navigation Group based on the navigation ID
15080     * if one does not exist , it will get created.
15081     * @param {string} the navgroup to add
15082     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15083     */
15084     get: function(navId) {
15085         if (typeof(this.groups[navId]) == 'undefined') {
15086             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15087         }
15088         return this.groups[navId] ;
15089     }
15090     
15091     
15092     
15093 });
15094
15095  /*
15096  * - LGPL
15097  *
15098  * TabPanel
15099  * 
15100  */
15101
15102 /**
15103  * @class Roo.bootstrap.TabPanel
15104  * @extends Roo.bootstrap.Component
15105  * Bootstrap TabPanel class
15106  * @cfg {Boolean} active panel active
15107  * @cfg {String} html panel content
15108  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15109  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15110  * 
15111  * 
15112  * @constructor
15113  * Create a new TabPanel
15114  * @param {Object} config The config object
15115  */
15116
15117 Roo.bootstrap.TabPanel = function(config){
15118     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15119     this.addEvents({
15120         /**
15121              * @event changed
15122              * Fires when the active status changes
15123              * @param {Roo.bootstrap.TabPanel} this
15124              * @param {Boolean} state the new state
15125             
15126          */
15127         'changed': true,
15128         /**
15129              * @event beforedeactivate
15130              * Fires before a tab is de-activated - can be used to do validation on a form.
15131              * @param {Roo.bootstrap.TabPanel} this
15132              * @return {Boolean} false if there is an error
15133             
15134          */
15135         'beforedeactivate': true
15136      });
15137     
15138     this.tabId = this.tabId || Roo.id();
15139   
15140 };
15141
15142 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15143     
15144     active: false,
15145     html: false,
15146     tabId: false,
15147     navId : false,
15148     
15149     getAutoCreate : function(){
15150         var cfg = {
15151             tag: 'div',
15152             // item is needed for carousel - not sure if it has any effect otherwise
15153             cls: 'tab-pane item',
15154             html: this.html || ''
15155         };
15156         
15157         if(this.active){
15158             cfg.cls += ' active';
15159         }
15160         
15161         if(this.tabId){
15162             cfg.tabId = this.tabId;
15163         }
15164         
15165         
15166         return cfg;
15167     },
15168     
15169     initEvents:  function()
15170     {
15171         Roo.log('-------- init events on tab panel ---------');
15172         
15173         var p = this.parent();
15174         this.navId = this.navId || p.navId;
15175         
15176         if (typeof(this.navId) != 'undefined') {
15177             // not really needed.. but just in case.. parent should be a NavGroup.
15178             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15179             Roo.log(['register', tg, this]);
15180             tg.register(this);
15181             
15182             var i = tg.tabs.length - 1;
15183             
15184             if(this.active && tg.bullets > 0 && i < tg.bullets){
15185                 tg.setActiveBullet(i);
15186             }
15187         }
15188         
15189     },
15190     
15191     
15192     onRender : function(ct, position)
15193     {
15194        // Roo.log("Call onRender: " + this.xtype);
15195         
15196         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15197         
15198         
15199         
15200         
15201         
15202     },
15203     
15204     setActive: function(state)
15205     {
15206         Roo.log("panel - set active " + this.tabId + "=" + state);
15207         
15208         this.active = state;
15209         if (!state) {
15210             this.el.removeClass('active');
15211             
15212         } else  if (!this.el.hasClass('active')) {
15213             this.el.addClass('active');
15214         }
15215         
15216         this.fireEvent('changed', this, state);
15217     }
15218     
15219     
15220 });
15221  
15222
15223  
15224
15225  /*
15226  * - LGPL
15227  *
15228  * DateField
15229  * 
15230  */
15231
15232 /**
15233  * @class Roo.bootstrap.DateField
15234  * @extends Roo.bootstrap.Input
15235  * Bootstrap DateField class
15236  * @cfg {Number} weekStart default 0
15237  * @cfg {String} viewMode default empty, (months|years)
15238  * @cfg {String} minViewMode default empty, (months|years)
15239  * @cfg {Number} startDate default -Infinity
15240  * @cfg {Number} endDate default Infinity
15241  * @cfg {Boolean} todayHighlight default false
15242  * @cfg {Boolean} todayBtn default false
15243  * @cfg {Boolean} calendarWeeks default false
15244  * @cfg {Object} daysOfWeekDisabled default empty
15245  * @cfg {Boolean} singleMode default false (true | false)
15246  * 
15247  * @cfg {Boolean} keyboardNavigation default true
15248  * @cfg {String} language default en
15249  * 
15250  * @constructor
15251  * Create a new DateField
15252  * @param {Object} config The config object
15253  */
15254
15255 Roo.bootstrap.DateField = function(config){
15256     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15257      this.addEvents({
15258             /**
15259              * @event show
15260              * Fires when this field show.
15261              * @param {Roo.bootstrap.DateField} this
15262              * @param {Mixed} date The date value
15263              */
15264             show : true,
15265             /**
15266              * @event show
15267              * Fires when this field hide.
15268              * @param {Roo.bootstrap.DateField} this
15269              * @param {Mixed} date The date value
15270              */
15271             hide : true,
15272             /**
15273              * @event select
15274              * Fires when select a date.
15275              * @param {Roo.bootstrap.DateField} this
15276              * @param {Mixed} date The date value
15277              */
15278             select : true
15279         });
15280 };
15281
15282 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15283     
15284     /**
15285      * @cfg {String} format
15286      * The default date format string which can be overriden for localization support.  The format must be
15287      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15288      */
15289     format : "m/d/y",
15290     /**
15291      * @cfg {String} altFormats
15292      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15293      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15294      */
15295     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15296     
15297     weekStart : 0,
15298     
15299     viewMode : '',
15300     
15301     minViewMode : '',
15302     
15303     todayHighlight : false,
15304     
15305     todayBtn: false,
15306     
15307     language: 'en',
15308     
15309     keyboardNavigation: true,
15310     
15311     calendarWeeks: false,
15312     
15313     startDate: -Infinity,
15314     
15315     endDate: Infinity,
15316     
15317     daysOfWeekDisabled: [],
15318     
15319     _events: [],
15320     
15321     singleMode : false,
15322     
15323     UTCDate: function()
15324     {
15325         return new Date(Date.UTC.apply(Date, arguments));
15326     },
15327     
15328     UTCToday: function()
15329     {
15330         var today = new Date();
15331         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15332     },
15333     
15334     getDate: function() {
15335             var d = this.getUTCDate();
15336             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15337     },
15338     
15339     getUTCDate: function() {
15340             return this.date;
15341     },
15342     
15343     setDate: function(d) {
15344             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15345     },
15346     
15347     setUTCDate: function(d) {
15348             this.date = d;
15349             this.setValue(this.formatDate(this.date));
15350     },
15351         
15352     onRender: function(ct, position)
15353     {
15354         
15355         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15356         
15357         this.language = this.language || 'en';
15358         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15359         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15360         
15361         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15362         this.format = this.format || 'm/d/y';
15363         this.isInline = false;
15364         this.isInput = true;
15365         this.component = this.el.select('.add-on', true).first() || false;
15366         this.component = (this.component && this.component.length === 0) ? false : this.component;
15367         this.hasInput = this.component && this.inputEL().length;
15368         
15369         if (typeof(this.minViewMode === 'string')) {
15370             switch (this.minViewMode) {
15371                 case 'months':
15372                     this.minViewMode = 1;
15373                     break;
15374                 case 'years':
15375                     this.minViewMode = 2;
15376                     break;
15377                 default:
15378                     this.minViewMode = 0;
15379                     break;
15380             }
15381         }
15382         
15383         if (typeof(this.viewMode === 'string')) {
15384             switch (this.viewMode) {
15385                 case 'months':
15386                     this.viewMode = 1;
15387                     break;
15388                 case 'years':
15389                     this.viewMode = 2;
15390                     break;
15391                 default:
15392                     this.viewMode = 0;
15393                     break;
15394             }
15395         }
15396                 
15397         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15398         
15399 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15400         
15401         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15402         
15403         this.picker().on('mousedown', this.onMousedown, this);
15404         this.picker().on('click', this.onClick, this);
15405         
15406         this.picker().addClass('datepicker-dropdown');
15407         
15408         this.startViewMode = this.viewMode;
15409         
15410         if(this.singleMode){
15411             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15412                 v.setVisibilityMode(Roo.Element.DISPLAY)
15413                 v.hide();
15414             });
15415             
15416             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15417                 v.setStyle('width', '189px');
15418             });
15419         }
15420         
15421         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15422             if(!this.calendarWeeks){
15423                 v.remove();
15424                 return;
15425             }
15426             
15427             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15428             v.attr('colspan', function(i, val){
15429                 return parseInt(val) + 1;
15430             });
15431         })
15432                         
15433         
15434         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15435         
15436         this.setStartDate(this.startDate);
15437         this.setEndDate(this.endDate);
15438         
15439         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15440         
15441         this.fillDow();
15442         this.fillMonths();
15443         this.update();
15444         this.showMode();
15445         
15446         if(this.isInline) {
15447             this.show();
15448         }
15449     },
15450     
15451     picker : function()
15452     {
15453         return this.pickerEl;
15454 //        return this.el.select('.datepicker', true).first();
15455     },
15456     
15457     fillDow: function()
15458     {
15459         var dowCnt = this.weekStart;
15460         
15461         var dow = {
15462             tag: 'tr',
15463             cn: [
15464                 
15465             ]
15466         };
15467         
15468         if(this.calendarWeeks){
15469             dow.cn.push({
15470                 tag: 'th',
15471                 cls: 'cw',
15472                 html: '&nbsp;'
15473             })
15474         }
15475         
15476         while (dowCnt < this.weekStart + 7) {
15477             dow.cn.push({
15478                 tag: 'th',
15479                 cls: 'dow',
15480                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15481             });
15482         }
15483         
15484         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15485     },
15486     
15487     fillMonths: function()
15488     {    
15489         var i = 0;
15490         var months = this.picker().select('>.datepicker-months td', true).first();
15491         
15492         months.dom.innerHTML = '';
15493         
15494         while (i < 12) {
15495             var month = {
15496                 tag: 'span',
15497                 cls: 'month',
15498                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15499             }
15500             
15501             months.createChild(month);
15502         }
15503         
15504     },
15505     
15506     update: function()
15507     {
15508         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;
15509         
15510         if (this.date < this.startDate) {
15511             this.viewDate = new Date(this.startDate);
15512         } else if (this.date > this.endDate) {
15513             this.viewDate = new Date(this.endDate);
15514         } else {
15515             this.viewDate = new Date(this.date);
15516         }
15517         
15518         this.fill();
15519     },
15520     
15521     fill: function() 
15522     {
15523         var d = new Date(this.viewDate),
15524                 year = d.getUTCFullYear(),
15525                 month = d.getUTCMonth(),
15526                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15527                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15528                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15529                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15530                 currentDate = this.date && this.date.valueOf(),
15531                 today = this.UTCToday();
15532         
15533         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15534         
15535 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15536         
15537 //        this.picker.select('>tfoot th.today').
15538 //                                              .text(dates[this.language].today)
15539 //                                              .toggle(this.todayBtn !== false);
15540     
15541         this.updateNavArrows();
15542         this.fillMonths();
15543                                                 
15544         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15545         
15546         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15547          
15548         prevMonth.setUTCDate(day);
15549         
15550         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15551         
15552         var nextMonth = new Date(prevMonth);
15553         
15554         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15555         
15556         nextMonth = nextMonth.valueOf();
15557         
15558         var fillMonths = false;
15559         
15560         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15561         
15562         while(prevMonth.valueOf() < nextMonth) {
15563             var clsName = '';
15564             
15565             if (prevMonth.getUTCDay() === this.weekStart) {
15566                 if(fillMonths){
15567                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15568                 }
15569                     
15570                 fillMonths = {
15571                     tag: 'tr',
15572                     cn: []
15573                 };
15574                 
15575                 if(this.calendarWeeks){
15576                     // ISO 8601: First week contains first thursday.
15577                     // ISO also states week starts on Monday, but we can be more abstract here.
15578                     var
15579                     // Start of current week: based on weekstart/current date
15580                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15581                     // Thursday of this week
15582                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15583                     // First Thursday of year, year from thursday
15584                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15585                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15586                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15587                     
15588                     fillMonths.cn.push({
15589                         tag: 'td',
15590                         cls: 'cw',
15591                         html: calWeek
15592                     });
15593                 }
15594             }
15595             
15596             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15597                 clsName += ' old';
15598             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15599                 clsName += ' new';
15600             }
15601             if (this.todayHighlight &&
15602                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15603                 prevMonth.getUTCMonth() == today.getMonth() &&
15604                 prevMonth.getUTCDate() == today.getDate()) {
15605                 clsName += ' today';
15606             }
15607             
15608             if (currentDate && prevMonth.valueOf() === currentDate) {
15609                 clsName += ' active';
15610             }
15611             
15612             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15613                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15614                     clsName += ' disabled';
15615             }
15616             
15617             fillMonths.cn.push({
15618                 tag: 'td',
15619                 cls: 'day ' + clsName,
15620                 html: prevMonth.getDate()
15621             })
15622             
15623             prevMonth.setDate(prevMonth.getDate()+1);
15624         }
15625           
15626         var currentYear = this.date && this.date.getUTCFullYear();
15627         var currentMonth = this.date && this.date.getUTCMonth();
15628         
15629         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15630         
15631         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15632             v.removeClass('active');
15633             
15634             if(currentYear === year && k === currentMonth){
15635                 v.addClass('active');
15636             }
15637             
15638             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15639                 v.addClass('disabled');
15640             }
15641             
15642         });
15643         
15644         
15645         year = parseInt(year/10, 10) * 10;
15646         
15647         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15648         
15649         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15650         
15651         year -= 1;
15652         for (var i = -1; i < 11; i++) {
15653             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15654                 tag: 'span',
15655                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15656                 html: year
15657             })
15658             
15659             year += 1;
15660         }
15661     },
15662     
15663     showMode: function(dir) 
15664     {
15665         if (dir) {
15666             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15667         }
15668         
15669         Roo.each(this.picker().select('>div',true).elements, function(v){
15670             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15671             v.hide();
15672         });
15673         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15674     },
15675     
15676     place: function()
15677     {
15678         if(this.isInline) return;
15679         
15680         this.picker().removeClass(['bottom', 'top']);
15681         
15682         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15683             /*
15684              * place to the top of element!
15685              *
15686              */
15687             
15688             this.picker().addClass('top');
15689             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15690             
15691             return;
15692         }
15693         
15694         this.picker().addClass('bottom');
15695         
15696         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15697     },
15698     
15699     parseDate : function(value)
15700     {
15701         if(!value || value instanceof Date){
15702             return value;
15703         }
15704         var v = Date.parseDate(value, this.format);
15705         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15706             v = Date.parseDate(value, 'Y-m-d');
15707         }
15708         if(!v && this.altFormats){
15709             if(!this.altFormatsArray){
15710                 this.altFormatsArray = this.altFormats.split("|");
15711             }
15712             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15713                 v = Date.parseDate(value, this.altFormatsArray[i]);
15714             }
15715         }
15716         return v;
15717     },
15718     
15719     formatDate : function(date, fmt)
15720     {   
15721         return (!date || !(date instanceof Date)) ?
15722         date : date.dateFormat(fmt || this.format);
15723     },
15724     
15725     onFocus : function()
15726     {
15727         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15728         this.show();
15729     },
15730     
15731     onBlur : function()
15732     {
15733         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15734         
15735         var d = this.inputEl().getValue();
15736         
15737         this.setValue(d);
15738                 
15739         this.hide();
15740     },
15741     
15742     show : function()
15743     {
15744         this.picker().show();
15745         this.update();
15746         this.place();
15747         
15748         this.fireEvent('show', this, this.date);
15749     },
15750     
15751     hide : function()
15752     {
15753         if(this.isInline) return;
15754         this.picker().hide();
15755         this.viewMode = this.startViewMode;
15756         this.showMode();
15757         
15758         this.fireEvent('hide', this, this.date);
15759         
15760     },
15761     
15762     onMousedown: function(e)
15763     {
15764         e.stopPropagation();
15765         e.preventDefault();
15766     },
15767     
15768     keyup: function(e)
15769     {
15770         Roo.bootstrap.DateField.superclass.keyup.call(this);
15771         this.update();
15772     },
15773
15774     setValue: function(v)
15775     {
15776         
15777         // v can be a string or a date..
15778         
15779         
15780         var d = new Date(this.parseDate(v) ).clearTime();
15781         
15782         if(isNaN(d.getTime())){
15783             this.date = this.viewDate = '';
15784             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15785             return;
15786         }
15787         
15788         v = this.formatDate(d);
15789         
15790         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15791         
15792         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15793      
15794         this.update();
15795
15796         this.fireEvent('select', this, this.date);
15797         
15798     },
15799     
15800     getValue: function()
15801     {
15802         return this.formatDate(this.date);
15803     },
15804     
15805     fireKey: function(e)
15806     {
15807         if (!this.picker().isVisible()){
15808             if (e.keyCode == 27) // allow escape to hide and re-show picker
15809                 this.show();
15810             return;
15811         }
15812         
15813         var dateChanged = false,
15814         dir, day, month,
15815         newDate, newViewDate;
15816         
15817         switch(e.keyCode){
15818             case 27: // escape
15819                 this.hide();
15820                 e.preventDefault();
15821                 break;
15822             case 37: // left
15823             case 39: // right
15824                 if (!this.keyboardNavigation) break;
15825                 dir = e.keyCode == 37 ? -1 : 1;
15826                 
15827                 if (e.ctrlKey){
15828                     newDate = this.moveYear(this.date, dir);
15829                     newViewDate = this.moveYear(this.viewDate, dir);
15830                 } else if (e.shiftKey){
15831                     newDate = this.moveMonth(this.date, dir);
15832                     newViewDate = this.moveMonth(this.viewDate, dir);
15833                 } else {
15834                     newDate = new Date(this.date);
15835                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15836                     newViewDate = new Date(this.viewDate);
15837                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15838                 }
15839                 if (this.dateWithinRange(newDate)){
15840                     this.date = newDate;
15841                     this.viewDate = newViewDate;
15842                     this.setValue(this.formatDate(this.date));
15843 //                    this.update();
15844                     e.preventDefault();
15845                     dateChanged = true;
15846                 }
15847                 break;
15848             case 38: // up
15849             case 40: // down
15850                 if (!this.keyboardNavigation) break;
15851                 dir = e.keyCode == 38 ? -1 : 1;
15852                 if (e.ctrlKey){
15853                     newDate = this.moveYear(this.date, dir);
15854                     newViewDate = this.moveYear(this.viewDate, dir);
15855                 } else if (e.shiftKey){
15856                     newDate = this.moveMonth(this.date, dir);
15857                     newViewDate = this.moveMonth(this.viewDate, dir);
15858                 } else {
15859                     newDate = new Date(this.date);
15860                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15861                     newViewDate = new Date(this.viewDate);
15862                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15863                 }
15864                 if (this.dateWithinRange(newDate)){
15865                     this.date = newDate;
15866                     this.viewDate = newViewDate;
15867                     this.setValue(this.formatDate(this.date));
15868 //                    this.update();
15869                     e.preventDefault();
15870                     dateChanged = true;
15871                 }
15872                 break;
15873             case 13: // enter
15874                 this.setValue(this.formatDate(this.date));
15875                 this.hide();
15876                 e.preventDefault();
15877                 break;
15878             case 9: // tab
15879                 this.setValue(this.formatDate(this.date));
15880                 this.hide();
15881                 break;
15882             case 16: // shift
15883             case 17: // ctrl
15884             case 18: // alt
15885                 break;
15886             default :
15887                 this.hide();
15888                 
15889         }
15890     },
15891     
15892     
15893     onClick: function(e) 
15894     {
15895         e.stopPropagation();
15896         e.preventDefault();
15897         
15898         var target = e.getTarget();
15899         
15900         if(target.nodeName.toLowerCase() === 'i'){
15901             target = Roo.get(target).dom.parentNode;
15902         }
15903         
15904         var nodeName = target.nodeName;
15905         var className = target.className;
15906         var html = target.innerHTML;
15907         //Roo.log(nodeName);
15908         
15909         switch(nodeName.toLowerCase()) {
15910             case 'th':
15911                 switch(className) {
15912                     case 'switch':
15913                         this.showMode(1);
15914                         break;
15915                     case 'prev':
15916                     case 'next':
15917                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15918                         switch(this.viewMode){
15919                                 case 0:
15920                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15921                                         break;
15922                                 case 1:
15923                                 case 2:
15924                                         this.viewDate = this.moveYear(this.viewDate, dir);
15925                                         break;
15926                         }
15927                         this.fill();
15928                         break;
15929                     case 'today':
15930                         var date = new Date();
15931                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15932 //                        this.fill()
15933                         this.setValue(this.formatDate(this.date));
15934                         
15935                         this.hide();
15936                         break;
15937                 }
15938                 break;
15939             case 'span':
15940                 if (className.indexOf('disabled') < 0) {
15941                     this.viewDate.setUTCDate(1);
15942                     if (className.indexOf('month') > -1) {
15943                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15944                     } else {
15945                         var year = parseInt(html, 10) || 0;
15946                         this.viewDate.setUTCFullYear(year);
15947                         
15948                     }
15949                     
15950                     if(this.singleMode){
15951                         this.setValue(this.formatDate(this.viewDate));
15952                         this.hide();
15953                         return;
15954                     }
15955                     
15956                     this.showMode(-1);
15957                     this.fill();
15958                 }
15959                 break;
15960                 
15961             case 'td':
15962                 //Roo.log(className);
15963                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15964                     var day = parseInt(html, 10) || 1;
15965                     var year = this.viewDate.getUTCFullYear(),
15966                         month = this.viewDate.getUTCMonth();
15967
15968                     if (className.indexOf('old') > -1) {
15969                         if(month === 0 ){
15970                             month = 11;
15971                             year -= 1;
15972                         }else{
15973                             month -= 1;
15974                         }
15975                     } else if (className.indexOf('new') > -1) {
15976                         if (month == 11) {
15977                             month = 0;
15978                             year += 1;
15979                         } else {
15980                             month += 1;
15981                         }
15982                     }
15983                     //Roo.log([year,month,day]);
15984                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15985                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15986 //                    this.fill();
15987                     //Roo.log(this.formatDate(this.date));
15988                     this.setValue(this.formatDate(this.date));
15989                     this.hide();
15990                 }
15991                 break;
15992         }
15993     },
15994     
15995     setStartDate: function(startDate)
15996     {
15997         this.startDate = startDate || -Infinity;
15998         if (this.startDate !== -Infinity) {
15999             this.startDate = this.parseDate(this.startDate);
16000         }
16001         this.update();
16002         this.updateNavArrows();
16003     },
16004
16005     setEndDate: function(endDate)
16006     {
16007         this.endDate = endDate || Infinity;
16008         if (this.endDate !== Infinity) {
16009             this.endDate = this.parseDate(this.endDate);
16010         }
16011         this.update();
16012         this.updateNavArrows();
16013     },
16014     
16015     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16016     {
16017         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16018         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16019             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16020         }
16021         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16022             return parseInt(d, 10);
16023         });
16024         this.update();
16025         this.updateNavArrows();
16026     },
16027     
16028     updateNavArrows: function() 
16029     {
16030         if(this.singleMode){
16031             return;
16032         }
16033         
16034         var d = new Date(this.viewDate),
16035         year = d.getUTCFullYear(),
16036         month = d.getUTCMonth();
16037         
16038         Roo.each(this.picker().select('.prev', true).elements, function(v){
16039             v.show();
16040             switch (this.viewMode) {
16041                 case 0:
16042
16043                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16044                         v.hide();
16045                     }
16046                     break;
16047                 case 1:
16048                 case 2:
16049                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16050                         v.hide();
16051                     }
16052                     break;
16053             }
16054         });
16055         
16056         Roo.each(this.picker().select('.next', true).elements, function(v){
16057             v.show();
16058             switch (this.viewMode) {
16059                 case 0:
16060
16061                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16062                         v.hide();
16063                     }
16064                     break;
16065                 case 1:
16066                 case 2:
16067                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16068                         v.hide();
16069                     }
16070                     break;
16071             }
16072         })
16073     },
16074     
16075     moveMonth: function(date, dir)
16076     {
16077         if (!dir) return date;
16078         var new_date = new Date(date.valueOf()),
16079         day = new_date.getUTCDate(),
16080         month = new_date.getUTCMonth(),
16081         mag = Math.abs(dir),
16082         new_month, test;
16083         dir = dir > 0 ? 1 : -1;
16084         if (mag == 1){
16085             test = dir == -1
16086             // If going back one month, make sure month is not current month
16087             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16088             ? function(){
16089                 return new_date.getUTCMonth() == month;
16090             }
16091             // If going forward one month, make sure month is as expected
16092             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16093             : function(){
16094                 return new_date.getUTCMonth() != new_month;
16095             };
16096             new_month = month + dir;
16097             new_date.setUTCMonth(new_month);
16098             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16099             if (new_month < 0 || new_month > 11)
16100                 new_month = (new_month + 12) % 12;
16101         } else {
16102             // For magnitudes >1, move one month at a time...
16103             for (var i=0; i<mag; i++)
16104                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16105                 new_date = this.moveMonth(new_date, dir);
16106             // ...then reset the day, keeping it in the new month
16107             new_month = new_date.getUTCMonth();
16108             new_date.setUTCDate(day);
16109             test = function(){
16110                 return new_month != new_date.getUTCMonth();
16111             };
16112         }
16113         // Common date-resetting loop -- if date is beyond end of month, make it
16114         // end of month
16115         while (test()){
16116             new_date.setUTCDate(--day);
16117             new_date.setUTCMonth(new_month);
16118         }
16119         return new_date;
16120     },
16121
16122     moveYear: function(date, dir)
16123     {
16124         return this.moveMonth(date, dir*12);
16125     },
16126
16127     dateWithinRange: function(date)
16128     {
16129         return date >= this.startDate && date <= this.endDate;
16130     },
16131
16132     
16133     remove: function() 
16134     {
16135         this.picker().remove();
16136     }
16137    
16138 });
16139
16140 Roo.apply(Roo.bootstrap.DateField,  {
16141     
16142     head : {
16143         tag: 'thead',
16144         cn: [
16145         {
16146             tag: 'tr',
16147             cn: [
16148             {
16149                 tag: 'th',
16150                 cls: 'prev',
16151                 html: '<i class="fa fa-arrow-left"/>'
16152             },
16153             {
16154                 tag: 'th',
16155                 cls: 'switch',
16156                 colspan: '5'
16157             },
16158             {
16159                 tag: 'th',
16160                 cls: 'next',
16161                 html: '<i class="fa fa-arrow-right"/>'
16162             }
16163
16164             ]
16165         }
16166         ]
16167     },
16168     
16169     content : {
16170         tag: 'tbody',
16171         cn: [
16172         {
16173             tag: 'tr',
16174             cn: [
16175             {
16176                 tag: 'td',
16177                 colspan: '7'
16178             }
16179             ]
16180         }
16181         ]
16182     },
16183     
16184     footer : {
16185         tag: 'tfoot',
16186         cn: [
16187         {
16188             tag: 'tr',
16189             cn: [
16190             {
16191                 tag: 'th',
16192                 colspan: '7',
16193                 cls: 'today'
16194             }
16195                     
16196             ]
16197         }
16198         ]
16199     },
16200     
16201     dates:{
16202         en: {
16203             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16204             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16205             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16206             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16207             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16208             today: "Today"
16209         }
16210     },
16211     
16212     modes: [
16213     {
16214         clsName: 'days',
16215         navFnc: 'Month',
16216         navStep: 1
16217     },
16218     {
16219         clsName: 'months',
16220         navFnc: 'FullYear',
16221         navStep: 1
16222     },
16223     {
16224         clsName: 'years',
16225         navFnc: 'FullYear',
16226         navStep: 10
16227     }]
16228 });
16229
16230 Roo.apply(Roo.bootstrap.DateField,  {
16231   
16232     template : {
16233         tag: 'div',
16234         cls: 'datepicker dropdown-menu roo-dynamic',
16235         cn: [
16236         {
16237             tag: 'div',
16238             cls: 'datepicker-days',
16239             cn: [
16240             {
16241                 tag: 'table',
16242                 cls: 'table-condensed',
16243                 cn:[
16244                 Roo.bootstrap.DateField.head,
16245                 {
16246                     tag: 'tbody'
16247                 },
16248                 Roo.bootstrap.DateField.footer
16249                 ]
16250             }
16251             ]
16252         },
16253         {
16254             tag: 'div',
16255             cls: 'datepicker-months',
16256             cn: [
16257             {
16258                 tag: 'table',
16259                 cls: 'table-condensed',
16260                 cn:[
16261                 Roo.bootstrap.DateField.head,
16262                 Roo.bootstrap.DateField.content,
16263                 Roo.bootstrap.DateField.footer
16264                 ]
16265             }
16266             ]
16267         },
16268         {
16269             tag: 'div',
16270             cls: 'datepicker-years',
16271             cn: [
16272             {
16273                 tag: 'table',
16274                 cls: 'table-condensed',
16275                 cn:[
16276                 Roo.bootstrap.DateField.head,
16277                 Roo.bootstrap.DateField.content,
16278                 Roo.bootstrap.DateField.footer
16279                 ]
16280             }
16281             ]
16282         }
16283         ]
16284     }
16285 });
16286
16287  
16288
16289  /*
16290  * - LGPL
16291  *
16292  * TimeField
16293  * 
16294  */
16295
16296 /**
16297  * @class Roo.bootstrap.TimeField
16298  * @extends Roo.bootstrap.Input
16299  * Bootstrap DateField class
16300  * 
16301  * 
16302  * @constructor
16303  * Create a new TimeField
16304  * @param {Object} config The config object
16305  */
16306
16307 Roo.bootstrap.TimeField = function(config){
16308     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16309     this.addEvents({
16310             /**
16311              * @event show
16312              * Fires when this field show.
16313              * @param {Roo.bootstrap.DateField} thisthis
16314              * @param {Mixed} date The date value
16315              */
16316             show : true,
16317             /**
16318              * @event show
16319              * Fires when this field hide.
16320              * @param {Roo.bootstrap.DateField} this
16321              * @param {Mixed} date The date value
16322              */
16323             hide : true,
16324             /**
16325              * @event select
16326              * Fires when select a date.
16327              * @param {Roo.bootstrap.DateField} this
16328              * @param {Mixed} date The date value
16329              */
16330             select : true
16331         });
16332 };
16333
16334 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16335     
16336     /**
16337      * @cfg {String} format
16338      * The default time format string which can be overriden for localization support.  The format must be
16339      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16340      */
16341     format : "H:i",
16342        
16343     onRender: function(ct, position)
16344     {
16345         
16346         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16347                 
16348         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16349         
16350         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16351         
16352         this.pop = this.picker().select('>.datepicker-time',true).first();
16353         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16354         
16355         this.picker().on('mousedown', this.onMousedown, this);
16356         this.picker().on('click', this.onClick, this);
16357         
16358         this.picker().addClass('datepicker-dropdown');
16359     
16360         this.fillTime();
16361         this.update();
16362             
16363         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16364         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16365         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16366         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16367         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16368         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16369
16370     },
16371     
16372     fireKey: function(e){
16373         if (!this.picker().isVisible()){
16374             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16375                 this.show();
16376             }
16377             return;
16378         }
16379
16380         e.preventDefault();
16381         
16382         switch(e.keyCode){
16383             case 27: // escape
16384                 this.hide();
16385                 break;
16386             case 37: // left
16387             case 39: // right
16388                 this.onTogglePeriod();
16389                 break;
16390             case 38: // up
16391                 this.onIncrementMinutes();
16392                 break;
16393             case 40: // down
16394                 this.onDecrementMinutes();
16395                 break;
16396             case 13: // enter
16397             case 9: // tab
16398                 this.setTime();
16399                 break;
16400         }
16401     },
16402     
16403     onClick: function(e) {
16404         e.stopPropagation();
16405         e.preventDefault();
16406     },
16407     
16408     picker : function()
16409     {
16410         return this.el.select('.datepicker', true).first();
16411     },
16412     
16413     fillTime: function()
16414     {    
16415         var time = this.pop.select('tbody', true).first();
16416         
16417         time.dom.innerHTML = '';
16418         
16419         time.createChild({
16420             tag: 'tr',
16421             cn: [
16422                 {
16423                     tag: 'td',
16424                     cn: [
16425                         {
16426                             tag: 'a',
16427                             href: '#',
16428                             cls: 'btn',
16429                             cn: [
16430                                 {
16431                                     tag: 'span',
16432                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16433                                 }
16434                             ]
16435                         } 
16436                     ]
16437                 },
16438                 {
16439                     tag: 'td',
16440                     cls: 'separator'
16441                 },
16442                 {
16443                     tag: 'td',
16444                     cn: [
16445                         {
16446                             tag: 'a',
16447                             href: '#',
16448                             cls: 'btn',
16449                             cn: [
16450                                 {
16451                                     tag: 'span',
16452                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16453                                 }
16454                             ]
16455                         }
16456                     ]
16457                 },
16458                 {
16459                     tag: 'td',
16460                     cls: 'separator'
16461                 }
16462             ]
16463         });
16464         
16465         time.createChild({
16466             tag: 'tr',
16467             cn: [
16468                 {
16469                     tag: 'td',
16470                     cn: [
16471                         {
16472                             tag: 'span',
16473                             cls: 'timepicker-hour',
16474                             html: '00'
16475                         }  
16476                     ]
16477                 },
16478                 {
16479                     tag: 'td',
16480                     cls: 'separator',
16481                     html: ':'
16482                 },
16483                 {
16484                     tag: 'td',
16485                     cn: [
16486                         {
16487                             tag: 'span',
16488                             cls: 'timepicker-minute',
16489                             html: '00'
16490                         }  
16491                     ]
16492                 },
16493                 {
16494                     tag: 'td',
16495                     cls: 'separator'
16496                 },
16497                 {
16498                     tag: 'td',
16499                     cn: [
16500                         {
16501                             tag: 'button',
16502                             type: 'button',
16503                             cls: 'btn btn-primary period',
16504                             html: 'AM'
16505                             
16506                         }
16507                     ]
16508                 }
16509             ]
16510         });
16511         
16512         time.createChild({
16513             tag: 'tr',
16514             cn: [
16515                 {
16516                     tag: 'td',
16517                     cn: [
16518                         {
16519                             tag: 'a',
16520                             href: '#',
16521                             cls: 'btn',
16522                             cn: [
16523                                 {
16524                                     tag: 'span',
16525                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16526                                 }
16527                             ]
16528                         }
16529                     ]
16530                 },
16531                 {
16532                     tag: 'td',
16533                     cls: 'separator'
16534                 },
16535                 {
16536                     tag: 'td',
16537                     cn: [
16538                         {
16539                             tag: 'a',
16540                             href: '#',
16541                             cls: 'btn',
16542                             cn: [
16543                                 {
16544                                     tag: 'span',
16545                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16546                                 }
16547                             ]
16548                         }
16549                     ]
16550                 },
16551                 {
16552                     tag: 'td',
16553                     cls: 'separator'
16554                 }
16555             ]
16556         });
16557         
16558     },
16559     
16560     update: function()
16561     {
16562         
16563         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16564         
16565         this.fill();
16566     },
16567     
16568     fill: function() 
16569     {
16570         var hours = this.time.getHours();
16571         var minutes = this.time.getMinutes();
16572         var period = 'AM';
16573         
16574         if(hours > 11){
16575             period = 'PM';
16576         }
16577         
16578         if(hours == 0){
16579             hours = 12;
16580         }
16581         
16582         
16583         if(hours > 12){
16584             hours = hours - 12;
16585         }
16586         
16587         if(hours < 10){
16588             hours = '0' + hours;
16589         }
16590         
16591         if(minutes < 10){
16592             minutes = '0' + minutes;
16593         }
16594         
16595         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16596         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16597         this.pop.select('button', true).first().dom.innerHTML = period;
16598         
16599     },
16600     
16601     place: function()
16602     {   
16603         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16604         
16605         var cls = ['bottom'];
16606         
16607         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16608             cls.pop();
16609             cls.push('top');
16610         }
16611         
16612         cls.push('right');
16613         
16614         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16615             cls.pop();
16616             cls.push('left');
16617         }
16618         
16619         this.picker().addClass(cls.join('-'));
16620         
16621         var _this = this;
16622         
16623         Roo.each(cls, function(c){
16624             if(c == 'bottom'){
16625                 _this.picker().setTop(_this.inputEl().getHeight());
16626                 return;
16627             }
16628             if(c == 'top'){
16629                 _this.picker().setTop(0 - _this.picker().getHeight());
16630                 return;
16631             }
16632             
16633             if(c == 'left'){
16634                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16635                 return;
16636             }
16637             if(c == 'right'){
16638                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16639                 return;
16640             }
16641         });
16642         
16643     },
16644   
16645     onFocus : function()
16646     {
16647         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16648         this.show();
16649     },
16650     
16651     onBlur : function()
16652     {
16653         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16654         this.hide();
16655     },
16656     
16657     show : function()
16658     {
16659         this.picker().show();
16660         this.pop.show();
16661         this.update();
16662         this.place();
16663         
16664         this.fireEvent('show', this, this.date);
16665     },
16666     
16667     hide : function()
16668     {
16669         this.picker().hide();
16670         this.pop.hide();
16671         
16672         this.fireEvent('hide', this, this.date);
16673     },
16674     
16675     setTime : function()
16676     {
16677         this.hide();
16678         this.setValue(this.time.format(this.format));
16679         
16680         this.fireEvent('select', this, this.date);
16681         
16682         
16683     },
16684     
16685     onMousedown: function(e){
16686         e.stopPropagation();
16687         e.preventDefault();
16688     },
16689     
16690     onIncrementHours: function()
16691     {
16692         Roo.log('onIncrementHours');
16693         this.time = this.time.add(Date.HOUR, 1);
16694         this.update();
16695         
16696     },
16697     
16698     onDecrementHours: function()
16699     {
16700         Roo.log('onDecrementHours');
16701         this.time = this.time.add(Date.HOUR, -1);
16702         this.update();
16703     },
16704     
16705     onIncrementMinutes: function()
16706     {
16707         Roo.log('onIncrementMinutes');
16708         this.time = this.time.add(Date.MINUTE, 1);
16709         this.update();
16710     },
16711     
16712     onDecrementMinutes: function()
16713     {
16714         Roo.log('onDecrementMinutes');
16715         this.time = this.time.add(Date.MINUTE, -1);
16716         this.update();
16717     },
16718     
16719     onTogglePeriod: function()
16720     {
16721         Roo.log('onTogglePeriod');
16722         this.time = this.time.add(Date.HOUR, 12);
16723         this.update();
16724     }
16725     
16726    
16727 });
16728
16729 Roo.apply(Roo.bootstrap.TimeField,  {
16730     
16731     content : {
16732         tag: 'tbody',
16733         cn: [
16734             {
16735                 tag: 'tr',
16736                 cn: [
16737                 {
16738                     tag: 'td',
16739                     colspan: '7'
16740                 }
16741                 ]
16742             }
16743         ]
16744     },
16745     
16746     footer : {
16747         tag: 'tfoot',
16748         cn: [
16749             {
16750                 tag: 'tr',
16751                 cn: [
16752                 {
16753                     tag: 'th',
16754                     colspan: '7',
16755                     cls: '',
16756                     cn: [
16757                         {
16758                             tag: 'button',
16759                             cls: 'btn btn-info ok',
16760                             html: 'OK'
16761                         }
16762                     ]
16763                 }
16764
16765                 ]
16766             }
16767         ]
16768     }
16769 });
16770
16771 Roo.apply(Roo.bootstrap.TimeField,  {
16772   
16773     template : {
16774         tag: 'div',
16775         cls: 'datepicker dropdown-menu',
16776         cn: [
16777             {
16778                 tag: 'div',
16779                 cls: 'datepicker-time',
16780                 cn: [
16781                 {
16782                     tag: 'table',
16783                     cls: 'table-condensed',
16784                     cn:[
16785                     Roo.bootstrap.TimeField.content,
16786                     Roo.bootstrap.TimeField.footer
16787                     ]
16788                 }
16789                 ]
16790             }
16791         ]
16792     }
16793 });
16794
16795  
16796
16797  /*
16798  * - LGPL
16799  *
16800  * MonthField
16801  * 
16802  */
16803
16804 /**
16805  * @class Roo.bootstrap.MonthField
16806  * @extends Roo.bootstrap.Input
16807  * Bootstrap MonthField class
16808  * 
16809  * @cfg {String} language default en
16810  * 
16811  * @constructor
16812  * Create a new MonthField
16813  * @param {Object} config The config object
16814  */
16815
16816 Roo.bootstrap.MonthField = function(config){
16817     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16818     
16819     this.addEvents({
16820         /**
16821          * @event show
16822          * Fires when this field show.
16823          * @param {Roo.bootstrap.MonthField} this
16824          * @param {Mixed} date The date value
16825          */
16826         show : true,
16827         /**
16828          * @event show
16829          * Fires when this field hide.
16830          * @param {Roo.bootstrap.MonthField} this
16831          * @param {Mixed} date The date value
16832          */
16833         hide : true,
16834         /**
16835          * @event select
16836          * Fires when select a date.
16837          * @param {Roo.bootstrap.MonthField} this
16838          * @param {String} oldvalue The old value
16839          * @param {String} newvalue The new value
16840          */
16841         select : true
16842     });
16843 };
16844
16845 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16846     
16847     onRender: function(ct, position)
16848     {
16849         
16850         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16851         
16852         this.language = this.language || 'en';
16853         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16854         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16855         
16856         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16857         this.isInline = false;
16858         this.isInput = true;
16859         this.component = this.el.select('.add-on', true).first() || false;
16860         this.component = (this.component && this.component.length === 0) ? false : this.component;
16861         this.hasInput = this.component && this.inputEL().length;
16862         
16863         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16864         
16865         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16866         
16867         this.picker().on('mousedown', this.onMousedown, this);
16868         this.picker().on('click', this.onClick, this);
16869         
16870         this.picker().addClass('datepicker-dropdown');
16871         
16872         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16873             v.setStyle('width', '189px');
16874         });
16875         
16876         this.fillMonths();
16877         
16878         this.update();
16879         
16880         if(this.isInline) {
16881             this.show();
16882         }
16883         
16884     },
16885     
16886     setValue: function(v, suppressEvent)
16887     {   
16888         var o = this.getValue();
16889         
16890         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16891         
16892         this.update();
16893
16894         if(suppressEvent !== true){
16895             this.fireEvent('select', this, o, v);
16896         }
16897         
16898     },
16899     
16900     getValue: function()
16901     {
16902         return this.value;
16903     },
16904     
16905     onClick: function(e) 
16906     {
16907         e.stopPropagation();
16908         e.preventDefault();
16909         
16910         var target = e.getTarget();
16911         
16912         if(target.nodeName.toLowerCase() === 'i'){
16913             target = Roo.get(target).dom.parentNode;
16914         }
16915         
16916         var nodeName = target.nodeName;
16917         var className = target.className;
16918         var html = target.innerHTML;
16919         
16920         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16921             return;
16922         }
16923         
16924         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16925         
16926         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16927         
16928         this.hide();
16929                         
16930     },
16931     
16932     picker : function()
16933     {
16934         return this.pickerEl;
16935     },
16936     
16937     fillMonths: function()
16938     {    
16939         var i = 0;
16940         var months = this.picker().select('>.datepicker-months td', true).first();
16941         
16942         months.dom.innerHTML = '';
16943         
16944         while (i < 12) {
16945             var month = {
16946                 tag: 'span',
16947                 cls: 'month',
16948                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16949             }
16950             
16951             months.createChild(month);
16952         }
16953         
16954     },
16955     
16956     update: function()
16957     {
16958         var _this = this;
16959         
16960         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16961             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16962         }
16963         
16964         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16965             e.removeClass('active');
16966             
16967             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16968                 e.addClass('active');
16969             }
16970         })
16971     },
16972     
16973     place: function()
16974     {
16975         if(this.isInline) return;
16976         
16977         this.picker().removeClass(['bottom', 'top']);
16978         
16979         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16980             /*
16981              * place to the top of element!
16982              *
16983              */
16984             
16985             this.picker().addClass('top');
16986             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16987             
16988             return;
16989         }
16990         
16991         this.picker().addClass('bottom');
16992         
16993         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16994     },
16995     
16996     onFocus : function()
16997     {
16998         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16999         this.show();
17000     },
17001     
17002     onBlur : function()
17003     {
17004         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17005         
17006         var d = this.inputEl().getValue();
17007         
17008         this.setValue(d);
17009                 
17010         this.hide();
17011     },
17012     
17013     show : function()
17014     {
17015         this.picker().show();
17016         this.picker().select('>.datepicker-months', true).first().show();
17017         this.update();
17018         this.place();
17019         
17020         this.fireEvent('show', this, this.date);
17021     },
17022     
17023     hide : function()
17024     {
17025         if(this.isInline) return;
17026         this.picker().hide();
17027         this.fireEvent('hide', this, this.date);
17028         
17029     },
17030     
17031     onMousedown: function(e)
17032     {
17033         e.stopPropagation();
17034         e.preventDefault();
17035     },
17036     
17037     keyup: function(e)
17038     {
17039         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17040         this.update();
17041     },
17042
17043     fireKey: function(e)
17044     {
17045         if (!this.picker().isVisible()){
17046             if (e.keyCode == 27) // allow escape to hide and re-show picker
17047                 this.show();
17048             return;
17049         }
17050         
17051         var dir;
17052         
17053         switch(e.keyCode){
17054             case 27: // escape
17055                 this.hide();
17056                 e.preventDefault();
17057                 break;
17058             case 37: // left
17059             case 39: // right
17060                 dir = e.keyCode == 37 ? -1 : 1;
17061                 
17062                 this.vIndex = this.vIndex + dir;
17063                 
17064                 if(this.vIndex < 0){
17065                     this.vIndex = 0;
17066                 }
17067                 
17068                 if(this.vIndex > 11){
17069                     this.vIndex = 11;
17070                 }
17071                 
17072                 if(isNaN(this.vIndex)){
17073                     this.vIndex = 0;
17074                 }
17075                 
17076                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17077                 
17078                 break;
17079             case 38: // up
17080             case 40: // down
17081                 
17082                 dir = e.keyCode == 38 ? -1 : 1;
17083                 
17084                 this.vIndex = this.vIndex + dir * 4;
17085                 
17086                 if(this.vIndex < 0){
17087                     this.vIndex = 0;
17088                 }
17089                 
17090                 if(this.vIndex > 11){
17091                     this.vIndex = 11;
17092                 }
17093                 
17094                 if(isNaN(this.vIndex)){
17095                     this.vIndex = 0;
17096                 }
17097                 
17098                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17099                 break;
17100                 
17101             case 13: // enter
17102                 
17103                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17104                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17105                 }
17106                 
17107                 this.hide();
17108                 e.preventDefault();
17109                 break;
17110             case 9: // tab
17111                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17112                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17113                 }
17114                 this.hide();
17115                 break;
17116             case 16: // shift
17117             case 17: // ctrl
17118             case 18: // alt
17119                 break;
17120             default :
17121                 this.hide();
17122                 
17123         }
17124     },
17125     
17126     remove: function() 
17127     {
17128         this.picker().remove();
17129     }
17130    
17131 });
17132
17133 Roo.apply(Roo.bootstrap.MonthField,  {
17134     
17135     content : {
17136         tag: 'tbody',
17137         cn: [
17138         {
17139             tag: 'tr',
17140             cn: [
17141             {
17142                 tag: 'td',
17143                 colspan: '7'
17144             }
17145             ]
17146         }
17147         ]
17148     },
17149     
17150     dates:{
17151         en: {
17152             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17153             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17154         }
17155     }
17156 });
17157
17158 Roo.apply(Roo.bootstrap.MonthField,  {
17159   
17160     template : {
17161         tag: 'div',
17162         cls: 'datepicker dropdown-menu roo-dynamic',
17163         cn: [
17164             {
17165                 tag: 'div',
17166                 cls: 'datepicker-months',
17167                 cn: [
17168                 {
17169                     tag: 'table',
17170                     cls: 'table-condensed',
17171                     cn:[
17172                         Roo.bootstrap.DateField.content
17173                     ]
17174                 }
17175                 ]
17176             }
17177         ]
17178     }
17179 });
17180
17181  
17182
17183  
17184  /*
17185  * - LGPL
17186  *
17187  * CheckBox
17188  * 
17189  */
17190
17191 /**
17192  * @class Roo.bootstrap.CheckBox
17193  * @extends Roo.bootstrap.Input
17194  * Bootstrap CheckBox class
17195  * 
17196  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17197  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17198  * @cfg {String} boxLabel The text that appears beside the checkbox
17199  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17200  * @cfg {Boolean} checked initnal the element
17201  * @cfg {Boolean} inline inline the element (default false)
17202  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17203  * 
17204  * @constructor
17205  * Create a new CheckBox
17206  * @param {Object} config The config object
17207  */
17208
17209 Roo.bootstrap.CheckBox = function(config){
17210     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17211    
17212     this.addEvents({
17213         /**
17214         * @event check
17215         * Fires when the element is checked or unchecked.
17216         * @param {Roo.bootstrap.CheckBox} this This input
17217         * @param {Boolean} checked The new checked value
17218         */
17219        check : true
17220     });
17221     
17222 };
17223
17224 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17225   
17226     inputType: 'checkbox',
17227     inputValue: 1,
17228     valueOff: 0,
17229     boxLabel: false,
17230     checked: false,
17231     weight : false,
17232     inline: false,
17233     
17234     getAutoCreate : function()
17235     {
17236         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17237         
17238         var id = Roo.id();
17239         
17240         var cfg = {};
17241         
17242         cfg.cls = 'form-group ' + this.inputType; //input-group
17243         
17244         if(this.inline){
17245             cfg.cls += ' ' + this.inputType + '-inline';
17246         }
17247         
17248         var input =  {
17249             tag: 'input',
17250             id : id,
17251             type : this.inputType,
17252             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17253             cls : 'roo-' + this.inputType, //'form-box',
17254             placeholder : this.placeholder || ''
17255             
17256         };
17257         
17258         if (this.weight) { // Validity check?
17259             cfg.cls += " " + this.inputType + "-" + this.weight;
17260         }
17261         
17262         if (this.disabled) {
17263             input.disabled=true;
17264         }
17265         
17266         if(this.checked){
17267             input.checked = this.checked;
17268         }
17269         
17270         if (this.name) {
17271             input.name = this.name;
17272         }
17273         
17274         if (this.size) {
17275             input.cls += ' input-' + this.size;
17276         }
17277         
17278         var settings=this;
17279         
17280         ['xs','sm','md','lg'].map(function(size){
17281             if (settings[size]) {
17282                 cfg.cls += ' col-' + size + '-' + settings[size];
17283             }
17284         });
17285         
17286         var inputblock = input;
17287          
17288         if (this.before || this.after) {
17289             
17290             inputblock = {
17291                 cls : 'input-group',
17292                 cn :  [] 
17293             };
17294             
17295             if (this.before) {
17296                 inputblock.cn.push({
17297                     tag :'span',
17298                     cls : 'input-group-addon',
17299                     html : this.before
17300                 });
17301             }
17302             
17303             inputblock.cn.push(input);
17304             
17305             if (this.after) {
17306                 inputblock.cn.push({
17307                     tag :'span',
17308                     cls : 'input-group-addon',
17309                     html : this.after
17310                 });
17311             }
17312             
17313         }
17314         
17315         if (align ==='left' && this.fieldLabel.length) {
17316                 Roo.log("left and has label");
17317                 cfg.cn = [
17318                     
17319                     {
17320                         tag: 'label',
17321                         'for' :  id,
17322                         cls : 'control-label col-md-' + this.labelWidth,
17323                         html : this.fieldLabel
17324                         
17325                     },
17326                     {
17327                         cls : "col-md-" + (12 - this.labelWidth), 
17328                         cn: [
17329                             inputblock
17330                         ]
17331                     }
17332                     
17333                 ];
17334         } else if ( this.fieldLabel.length) {
17335                 Roo.log(" label");
17336                 cfg.cn = [
17337                    
17338                     {
17339                         tag: this.boxLabel ? 'span' : 'label',
17340                         'for': id,
17341                         cls: 'control-label box-input-label',
17342                         //cls : 'input-group-addon',
17343                         html : this.fieldLabel
17344                         
17345                     },
17346                     
17347                     inputblock
17348                     
17349                 ];
17350
17351         } else {
17352             
17353                 Roo.log(" no label && no align");
17354                 cfg.cn = [  inputblock ] ;
17355                 
17356                 
17357         }
17358         if(this.boxLabel){
17359              var boxLabelCfg = {
17360                 tag: 'label',
17361                 //'for': id, // box label is handled by onclick - so no for...
17362                 cls: 'box-label',
17363                 html: this.boxLabel
17364             }
17365             
17366             if(this.tooltip){
17367                 boxLabelCfg.tooltip = this.tooltip;
17368             }
17369              
17370             cfg.cn.push(boxLabelCfg);
17371         }
17372         
17373         
17374        
17375         return cfg;
17376         
17377     },
17378     
17379     /**
17380      * return the real input element.
17381      */
17382     inputEl: function ()
17383     {
17384         return this.el.select('input.roo-' + this.inputType,true).first();
17385     },
17386     
17387     labelEl: function()
17388     {
17389         return this.el.select('label.control-label',true).first();
17390     },
17391     /* depricated... */
17392     
17393     label: function()
17394     {
17395         return this.labelEl();
17396     },
17397     
17398     initEvents : function()
17399     {
17400 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17401         
17402         this.inputEl().on('click', this.onClick,  this);
17403         
17404         if (this.boxLabel) { 
17405             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17406         }
17407         
17408         this.startValue = this.getValue();
17409         
17410         if(this.groupId){
17411             Roo.bootstrap.CheckBox.register(this);
17412         }
17413     },
17414     
17415     onClick : function()
17416     {   
17417         this.setChecked(!this.checked);
17418     },
17419     
17420     setChecked : function(state,suppressEvent)
17421     {
17422         this.startValue = this.getValue();
17423         
17424         if(this.inputType == 'radio'){
17425             
17426             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17427                 e.dom.checked = false;
17428             });
17429             
17430             this.inputEl().dom.checked = true;
17431             
17432             this.inputEl().dom.value = this.inputValue;
17433             
17434             if(suppressEvent !== true){
17435                 this.fireEvent('check', this, true);
17436             }
17437             
17438             this.validate();
17439             
17440             return;
17441         }
17442         
17443         this.checked = state;
17444         
17445         this.inputEl().dom.checked = state;
17446         
17447         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17448         
17449         if(suppressEvent !== true){
17450             this.fireEvent('check', this, state);
17451         }
17452         
17453         this.validate();
17454     },
17455     
17456     getValue : function()
17457     {
17458         if(this.inputType == 'radio'){
17459             return this.getGroupValue();
17460         }
17461         
17462         return this.inputEl().getValue();
17463         
17464     },
17465     
17466     getGroupValue : function()
17467     {
17468         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17469             return '';
17470         }
17471         
17472         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17473     },
17474     
17475     setValue : function(v,suppressEvent)
17476     {
17477         if(this.inputType == 'radio'){
17478             this.setGroupValue(v, suppressEvent);
17479             return;
17480         }
17481         
17482         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17483         
17484         this.validate();
17485     },
17486     
17487     setGroupValue : function(v, suppressEvent)
17488     {
17489         this.startValue = this.getValue();
17490         
17491         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17492             e.dom.checked = false;
17493             
17494             if(e.dom.value == v){
17495                 e.dom.checked = true;
17496             }
17497         });
17498         
17499         if(suppressEvent !== true){
17500             this.fireEvent('check', this, true);
17501         }
17502
17503         this.validate();
17504         
17505         return;
17506     },
17507     
17508     validate : function()
17509     {
17510         if(
17511                 this.disabled || 
17512                 (this.inputType == 'radio' && this.validateRadio()) ||
17513                 (this.inputType == 'checkbox' && this.validateCheckbox())
17514         ){
17515             this.markValid();
17516             return true;
17517         }
17518         
17519         this.markInvalid();
17520         return false;
17521     },
17522     
17523     validateRadio : function()
17524     {
17525         var valid = false;
17526         
17527         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17528             if(!e.dom.checked){
17529                 return;
17530             }
17531             
17532             valid = true;
17533             
17534             return false;
17535         });
17536         
17537         return valid;
17538     },
17539     
17540     validateCheckbox : function()
17541     {
17542         if(!this.groupId){
17543             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17544         }
17545         
17546         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17547         
17548         if(!group){
17549             return false;
17550         }
17551         
17552         var r = false;
17553         
17554         for(var i in group){
17555             if(r){
17556                 break;
17557             }
17558             
17559             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17560         }
17561         
17562         return r;
17563     },
17564     
17565     /**
17566      * Mark this field as valid
17567      */
17568     markValid : function()
17569     {
17570         if(this.allowBlank){
17571             return;
17572         }
17573         
17574         var _this = this;
17575         
17576         this.fireEvent('valid', this);
17577         
17578         if(this.inputType == 'radio'){
17579             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17580                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17581                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17582             });
17583             
17584             return;
17585         }
17586         
17587         if(!this.groupId){
17588             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17589             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17590             return;
17591         }
17592         
17593         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17594             
17595         if(!group){
17596             return;
17597         }
17598         
17599         for(var i in group){
17600             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17601             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17602         }
17603     },
17604     
17605      /**
17606      * Mark this field as invalid
17607      * @param {String} msg The validation message
17608      */
17609     markInvalid : function(msg)
17610     {
17611         if(this.allowBlank){
17612             return;
17613         }
17614         
17615         var _this = this;
17616         
17617         this.fireEvent('invalid', this, msg);
17618         
17619         if(this.inputType == 'radio'){
17620             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17621                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17622                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17623             });
17624             
17625             return;
17626         }
17627         
17628         if(!this.groupId){
17629             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17630             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17631             return;
17632         }
17633         
17634         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17635             
17636         if(!group){
17637             return;
17638         }
17639         
17640         for(var i in group){
17641             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17642             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17643         }
17644         
17645     }
17646     
17647 });
17648
17649 Roo.apply(Roo.bootstrap.CheckBox, {
17650     
17651     groups: {},
17652     
17653      /**
17654     * register a CheckBox Group
17655     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17656     */
17657     register : function(checkbox)
17658     {
17659         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17660             this.groups[checkbox.groupId] = {};
17661         }
17662         
17663         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17664             return;
17665         }
17666         
17667         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17668         
17669     },
17670     /**
17671     * fetch a CheckBox Group based on the group ID
17672     * @param {string} the group ID
17673     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17674     */
17675     get: function(groupId) {
17676         if (typeof(this.groups[groupId]) == 'undefined') {
17677             return false;
17678         }
17679         
17680         return this.groups[groupId] ;
17681     }
17682     
17683     
17684 });
17685 /*
17686  * - LGPL
17687  *
17688  * Radio
17689  *
17690  *
17691  * not inline
17692  *<div class="radio">
17693   <label>
17694     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17695     Option one is this and that&mdash;be sure to include why it's great
17696   </label>
17697 </div>
17698  *
17699  *
17700  *inline
17701  *<span>
17702  *<label class="radio-inline">fieldLabel</label>
17703  *<label class="radio-inline">
17704   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17705 </label>
17706 <span>
17707  * 
17708  * 
17709  */
17710
17711 /**
17712  * @class Roo.bootstrap.Radio
17713  * @extends Roo.bootstrap.CheckBox
17714  * Bootstrap Radio class
17715
17716  * @constructor
17717  * Create a new Radio
17718  * @param {Object} config The config object
17719  */
17720
17721 Roo.bootstrap.Radio = function(config){
17722     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17723    
17724 };
17725
17726 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17727     
17728     inputType: 'radio',
17729     inputValue: '',
17730     valueOff: '',
17731     
17732     getAutoCreate : function()
17733     {
17734         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17735         align = align || 'left'; // default...
17736         
17737         
17738         
17739         var id = Roo.id();
17740         
17741         var cfg = {
17742                 tag : this.inline ? 'span' : 'div',
17743                 cls : '',
17744                 cn : []
17745         };
17746         
17747         var inline = this.inline ? ' radio-inline' : '';
17748         
17749         var lbl = {
17750                 tag: 'label' ,
17751                 // does not need for, as we wrap the input with it..
17752                 'for' : id,
17753                 cls : 'control-label box-label' + inline,
17754                 cn : []
17755         };
17756         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17757         
17758         var fieldLabel = {
17759             tag: 'label' ,
17760             //cls : 'control-label' + inline,
17761             html : this.fieldLabel,
17762             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17763         };
17764         
17765  
17766         
17767         
17768         var input =  {
17769             tag: 'input',
17770             id : id,
17771             type : this.inputType,
17772             //value : (!this.checked) ? this.valueOff : this.inputValue,
17773             value : this.inputValue,
17774             cls : 'roo-radio',
17775             placeholder : this.placeholder || '' // ?? needed????
17776             
17777         };
17778         if (this.weight) { // Validity check?
17779             input.cls += " radio-" + this.weight;
17780         }
17781         if (this.disabled) {
17782             input.disabled=true;
17783         }
17784         
17785         if(this.checked){
17786             input.checked = this.checked;
17787         }
17788         
17789         if (this.name) {
17790             input.name = this.name;
17791         }
17792         
17793         if (this.size) {
17794             input.cls += ' input-' + this.size;
17795         }
17796         
17797         //?? can span's inline have a width??
17798         
17799         var settings=this;
17800         ['xs','sm','md','lg'].map(function(size){
17801             if (settings[size]) {
17802                 cfg.cls += ' col-' + size + '-' + settings[size];
17803             }
17804         });
17805         
17806         var inputblock = input;
17807         
17808         if (this.before || this.after) {
17809             
17810             inputblock = {
17811                 cls : 'input-group',
17812                 tag : 'span',
17813                 cn :  [] 
17814             };
17815             if (this.before) {
17816                 inputblock.cn.push({
17817                     tag :'span',
17818                     cls : 'input-group-addon',
17819                     html : this.before
17820                 });
17821             }
17822             inputblock.cn.push(input);
17823             if (this.after) {
17824                 inputblock.cn.push({
17825                     tag :'span',
17826                     cls : 'input-group-addon',
17827                     html : this.after
17828                 });
17829             }
17830             
17831         };
17832         
17833         
17834         if (this.fieldLabel && this.fieldLabel.length) {
17835             cfg.cn.push(fieldLabel);
17836         }
17837        
17838         // normal bootstrap puts the input inside the label.
17839         // however with our styled version - it has to go after the input.
17840        
17841         //lbl.cn.push(inputblock);
17842         
17843         var lblwrap =  {
17844             tag: 'span',
17845             cls: 'radio' + inline,
17846             cn: [
17847                 inputblock,
17848                 lbl
17849             ]
17850         };
17851         
17852         cfg.cn.push( lblwrap);
17853         
17854         if(this.boxLabel){
17855             lbl.cn.push({
17856                 tag: 'span',
17857                 html: this.boxLabel
17858             })
17859         }
17860          
17861         
17862         return cfg;
17863         
17864     },
17865     
17866     initEvents : function()
17867     {
17868 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17869         
17870         this.inputEl().on('click', this.onClick,  this);
17871         if (this.boxLabel) {
17872             Roo.log('find label')
17873             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17874         }
17875         
17876     },
17877     
17878     inputEl: function ()
17879     {
17880         return this.el.select('input.roo-radio',true).first();
17881     },
17882     onClick : function()
17883     {   
17884         Roo.log("click");
17885         this.setChecked(true);
17886     },
17887     
17888     setChecked : function(state,suppressEvent)
17889     {
17890         if(state){
17891             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17892                 v.dom.checked = false;
17893             });
17894         }
17895         Roo.log(this.inputEl().dom);
17896         this.checked = state;
17897         this.inputEl().dom.checked = state;
17898         
17899         if(suppressEvent !== true){
17900             this.fireEvent('check', this, state);
17901         }
17902         
17903         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17904         
17905     },
17906     
17907     getGroupValue : function()
17908     {
17909         var value = '';
17910         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17911             if(v.dom.checked == true){
17912                 value = v.dom.value;
17913             }
17914         });
17915         
17916         return value;
17917     },
17918     
17919     /**
17920      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17921      * @return {Mixed} value The field value
17922      */
17923     getValue : function(){
17924         return this.getGroupValue();
17925     }
17926     
17927 });
17928
17929  
17930 //<script type="text/javascript">
17931
17932 /*
17933  * Based  Ext JS Library 1.1.1
17934  * Copyright(c) 2006-2007, Ext JS, LLC.
17935  * LGPL
17936  *
17937  */
17938  
17939 /**
17940  * @class Roo.HtmlEditorCore
17941  * @extends Roo.Component
17942  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17943  *
17944  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17945  */
17946
17947 Roo.HtmlEditorCore = function(config){
17948     
17949     
17950     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17951     
17952     
17953     this.addEvents({
17954         /**
17955          * @event initialize
17956          * Fires when the editor is fully initialized (including the iframe)
17957          * @param {Roo.HtmlEditorCore} this
17958          */
17959         initialize: true,
17960         /**
17961          * @event activate
17962          * Fires when the editor is first receives the focus. Any insertion must wait
17963          * until after this event.
17964          * @param {Roo.HtmlEditorCore} this
17965          */
17966         activate: true,
17967          /**
17968          * @event beforesync
17969          * Fires before the textarea is updated with content from the editor iframe. Return false
17970          * to cancel the sync.
17971          * @param {Roo.HtmlEditorCore} this
17972          * @param {String} html
17973          */
17974         beforesync: true,
17975          /**
17976          * @event beforepush
17977          * Fires before the iframe editor is updated with content from the textarea. Return false
17978          * to cancel the push.
17979          * @param {Roo.HtmlEditorCore} this
17980          * @param {String} html
17981          */
17982         beforepush: true,
17983          /**
17984          * @event sync
17985          * Fires when the textarea is updated with content from the editor iframe.
17986          * @param {Roo.HtmlEditorCore} this
17987          * @param {String} html
17988          */
17989         sync: true,
17990          /**
17991          * @event push
17992          * Fires when the iframe editor is updated with content from the textarea.
17993          * @param {Roo.HtmlEditorCore} this
17994          * @param {String} html
17995          */
17996         push: true,
17997         
17998         /**
17999          * @event editorevent
18000          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18001          * @param {Roo.HtmlEditorCore} this
18002          */
18003         editorevent: true
18004         
18005     });
18006     
18007     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18008     
18009     // defaults : white / black...
18010     this.applyBlacklists();
18011     
18012     
18013     
18014 };
18015
18016
18017 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18018
18019
18020      /**
18021      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18022      */
18023     
18024     owner : false,
18025     
18026      /**
18027      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18028      *                        Roo.resizable.
18029      */
18030     resizable : false,
18031      /**
18032      * @cfg {Number} height (in pixels)
18033      */   
18034     height: 300,
18035    /**
18036      * @cfg {Number} width (in pixels)
18037      */   
18038     width: 500,
18039     
18040     /**
18041      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18042      * 
18043      */
18044     stylesheets: false,
18045     
18046     // id of frame..
18047     frameId: false,
18048     
18049     // private properties
18050     validationEvent : false,
18051     deferHeight: true,
18052     initialized : false,
18053     activated : false,
18054     sourceEditMode : false,
18055     onFocus : Roo.emptyFn,
18056     iframePad:3,
18057     hideMode:'offsets',
18058     
18059     clearUp: true,
18060     
18061     // blacklist + whitelisted elements..
18062     black: false,
18063     white: false,
18064      
18065     
18066
18067     /**
18068      * Protected method that will not generally be called directly. It
18069      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18070      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18071      */
18072     getDocMarkup : function(){
18073         // body styles..
18074         var st = '';
18075         
18076         // inherit styels from page...?? 
18077         if (this.stylesheets === false) {
18078             
18079             Roo.get(document.head).select('style').each(function(node) {
18080                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18081             });
18082             
18083             Roo.get(document.head).select('link').each(function(node) { 
18084                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18085             });
18086             
18087         } else if (!this.stylesheets.length) {
18088                 // simple..
18089                 st = '<style type="text/css">' +
18090                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18091                    '</style>';
18092         } else { 
18093             
18094         }
18095         
18096         st +=  '<style type="text/css">' +
18097             'IMG { cursor: pointer } ' +
18098         '</style>';
18099
18100         
18101         return '<html><head>' + st  +
18102             //<style type="text/css">' +
18103             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18104             //'</style>' +
18105             ' </head><body class="roo-htmleditor-body"></body></html>';
18106     },
18107
18108     // private
18109     onRender : function(ct, position)
18110     {
18111         var _t = this;
18112         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18113         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18114         
18115         
18116         this.el.dom.style.border = '0 none';
18117         this.el.dom.setAttribute('tabIndex', -1);
18118         this.el.addClass('x-hidden hide');
18119         
18120         
18121         
18122         if(Roo.isIE){ // fix IE 1px bogus margin
18123             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18124         }
18125        
18126         
18127         this.frameId = Roo.id();
18128         
18129          
18130         
18131         var iframe = this.owner.wrap.createChild({
18132             tag: 'iframe',
18133             cls: 'form-control', // bootstrap..
18134             id: this.frameId,
18135             name: this.frameId,
18136             frameBorder : 'no',
18137             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18138         }, this.el
18139         );
18140         
18141         
18142         this.iframe = iframe.dom;
18143
18144          this.assignDocWin();
18145         
18146         this.doc.designMode = 'on';
18147        
18148         this.doc.open();
18149         this.doc.write(this.getDocMarkup());
18150         this.doc.close();
18151
18152         
18153         var task = { // must defer to wait for browser to be ready
18154             run : function(){
18155                 //console.log("run task?" + this.doc.readyState);
18156                 this.assignDocWin();
18157                 if(this.doc.body || this.doc.readyState == 'complete'){
18158                     try {
18159                         this.doc.designMode="on";
18160                     } catch (e) {
18161                         return;
18162                     }
18163                     Roo.TaskMgr.stop(task);
18164                     this.initEditor.defer(10, this);
18165                 }
18166             },
18167             interval : 10,
18168             duration: 10000,
18169             scope: this
18170         };
18171         Roo.TaskMgr.start(task);
18172
18173     },
18174
18175     // private
18176     onResize : function(w, h)
18177     {
18178          Roo.log('resize: ' +w + ',' + h );
18179         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18180         if(!this.iframe){
18181             return;
18182         }
18183         if(typeof w == 'number'){
18184             
18185             this.iframe.style.width = w + 'px';
18186         }
18187         if(typeof h == 'number'){
18188             
18189             this.iframe.style.height = h + 'px';
18190             if(this.doc){
18191                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18192             }
18193         }
18194         
18195     },
18196
18197     /**
18198      * Toggles the editor between standard and source edit mode.
18199      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18200      */
18201     toggleSourceEdit : function(sourceEditMode){
18202         
18203         this.sourceEditMode = sourceEditMode === true;
18204         
18205         if(this.sourceEditMode){
18206  
18207             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18208             
18209         }else{
18210             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18211             //this.iframe.className = '';
18212             this.deferFocus();
18213         }
18214         //this.setSize(this.owner.wrap.getSize());
18215         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18216     },
18217
18218     
18219   
18220
18221     /**
18222      * Protected method that will not generally be called directly. If you need/want
18223      * custom HTML cleanup, this is the method you should override.
18224      * @param {String} html The HTML to be cleaned
18225      * return {String} The cleaned HTML
18226      */
18227     cleanHtml : function(html){
18228         html = String(html);
18229         if(html.length > 5){
18230             if(Roo.isSafari){ // strip safari nonsense
18231                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18232             }
18233         }
18234         if(html == '&nbsp;'){
18235             html = '';
18236         }
18237         return html;
18238     },
18239
18240     /**
18241      * HTML Editor -> Textarea
18242      * Protected method that will not generally be called directly. Syncs the contents
18243      * of the editor iframe with the textarea.
18244      */
18245     syncValue : function(){
18246         if(this.initialized){
18247             var bd = (this.doc.body || this.doc.documentElement);
18248             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18249             var html = bd.innerHTML;
18250             if(Roo.isSafari){
18251                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18252                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18253                 if(m && m[1]){
18254                     html = '<div style="'+m[0]+'">' + html + '</div>';
18255                 }
18256             }
18257             html = this.cleanHtml(html);
18258             // fix up the special chars.. normaly like back quotes in word...
18259             // however we do not want to do this with chinese..
18260             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18261                 var cc = b.charCodeAt();
18262                 if (
18263                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18264                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18265                     (cc >= 0xf900 && cc < 0xfb00 )
18266                 ) {
18267                         return b;
18268                 }
18269                 return "&#"+cc+";" 
18270             });
18271             if(this.owner.fireEvent('beforesync', this, html) !== false){
18272                 this.el.dom.value = html;
18273                 this.owner.fireEvent('sync', this, html);
18274             }
18275         }
18276     },
18277
18278     /**
18279      * Protected method that will not generally be called directly. Pushes the value of the textarea
18280      * into the iframe editor.
18281      */
18282     pushValue : function(){
18283         if(this.initialized){
18284             var v = this.el.dom.value.trim();
18285             
18286 //            if(v.length < 1){
18287 //                v = '&#160;';
18288 //            }
18289             
18290             if(this.owner.fireEvent('beforepush', this, v) !== false){
18291                 var d = (this.doc.body || this.doc.documentElement);
18292                 d.innerHTML = v;
18293                 this.cleanUpPaste();
18294                 this.el.dom.value = d.innerHTML;
18295                 this.owner.fireEvent('push', this, v);
18296             }
18297         }
18298     },
18299
18300     // private
18301     deferFocus : function(){
18302         this.focus.defer(10, this);
18303     },
18304
18305     // doc'ed in Field
18306     focus : function(){
18307         if(this.win && !this.sourceEditMode){
18308             this.win.focus();
18309         }else{
18310             this.el.focus();
18311         }
18312     },
18313     
18314     assignDocWin: function()
18315     {
18316         var iframe = this.iframe;
18317         
18318          if(Roo.isIE){
18319             this.doc = iframe.contentWindow.document;
18320             this.win = iframe.contentWindow;
18321         } else {
18322 //            if (!Roo.get(this.frameId)) {
18323 //                return;
18324 //            }
18325 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18326 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18327             
18328             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18329                 return;
18330             }
18331             
18332             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18333             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18334         }
18335     },
18336     
18337     // private
18338     initEditor : function(){
18339         //console.log("INIT EDITOR");
18340         this.assignDocWin();
18341         
18342         
18343         
18344         this.doc.designMode="on";
18345         this.doc.open();
18346         this.doc.write(this.getDocMarkup());
18347         this.doc.close();
18348         
18349         var dbody = (this.doc.body || this.doc.documentElement);
18350         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18351         // this copies styles from the containing element into thsi one..
18352         // not sure why we need all of this..
18353         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18354         
18355         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18356         //ss['background-attachment'] = 'fixed'; // w3c
18357         dbody.bgProperties = 'fixed'; // ie
18358         //Roo.DomHelper.applyStyles(dbody, ss);
18359         Roo.EventManager.on(this.doc, {
18360             //'mousedown': this.onEditorEvent,
18361             'mouseup': this.onEditorEvent,
18362             'dblclick': this.onEditorEvent,
18363             'click': this.onEditorEvent,
18364             'keyup': this.onEditorEvent,
18365             buffer:100,
18366             scope: this
18367         });
18368         if(Roo.isGecko){
18369             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18370         }
18371         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18372             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18373         }
18374         this.initialized = true;
18375
18376         this.owner.fireEvent('initialize', this);
18377         this.pushValue();
18378     },
18379
18380     // private
18381     onDestroy : function(){
18382         
18383         
18384         
18385         if(this.rendered){
18386             
18387             //for (var i =0; i < this.toolbars.length;i++) {
18388             //    // fixme - ask toolbars for heights?
18389             //    this.toolbars[i].onDestroy();
18390            // }
18391             
18392             //this.wrap.dom.innerHTML = '';
18393             //this.wrap.remove();
18394         }
18395     },
18396
18397     // private
18398     onFirstFocus : function(){
18399         
18400         this.assignDocWin();
18401         
18402         
18403         this.activated = true;
18404          
18405     
18406         if(Roo.isGecko){ // prevent silly gecko errors
18407             this.win.focus();
18408             var s = this.win.getSelection();
18409             if(!s.focusNode || s.focusNode.nodeType != 3){
18410                 var r = s.getRangeAt(0);
18411                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18412                 r.collapse(true);
18413                 this.deferFocus();
18414             }
18415             try{
18416                 this.execCmd('useCSS', true);
18417                 this.execCmd('styleWithCSS', false);
18418             }catch(e){}
18419         }
18420         this.owner.fireEvent('activate', this);
18421     },
18422
18423     // private
18424     adjustFont: function(btn){
18425         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18426         //if(Roo.isSafari){ // safari
18427         //    adjust *= 2;
18428        // }
18429         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18430         if(Roo.isSafari){ // safari
18431             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18432             v =  (v < 10) ? 10 : v;
18433             v =  (v > 48) ? 48 : v;
18434             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18435             
18436         }
18437         
18438         
18439         v = Math.max(1, v+adjust);
18440         
18441         this.execCmd('FontSize', v  );
18442     },
18443
18444     onEditorEvent : function(e){
18445         this.owner.fireEvent('editorevent', this, e);
18446       //  this.updateToolbar();
18447         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18448     },
18449
18450     insertTag : function(tg)
18451     {
18452         // could be a bit smarter... -> wrap the current selected tRoo..
18453         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18454             
18455             range = this.createRange(this.getSelection());
18456             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18457             wrappingNode.appendChild(range.extractContents());
18458             range.insertNode(wrappingNode);
18459
18460             return;
18461             
18462             
18463             
18464         }
18465         this.execCmd("formatblock",   tg);
18466         
18467     },
18468     
18469     insertText : function(txt)
18470     {
18471         
18472         
18473         var range = this.createRange();
18474         range.deleteContents();
18475                //alert(Sender.getAttribute('label'));
18476                
18477         range.insertNode(this.doc.createTextNode(txt));
18478     } ,
18479     
18480      
18481
18482     /**
18483      * Executes a Midas editor command on the editor document and performs necessary focus and
18484      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18485      * @param {String} cmd The Midas command
18486      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18487      */
18488     relayCmd : function(cmd, value){
18489         this.win.focus();
18490         this.execCmd(cmd, value);
18491         this.owner.fireEvent('editorevent', this);
18492         //this.updateToolbar();
18493         this.owner.deferFocus();
18494     },
18495
18496     /**
18497      * Executes a Midas editor command directly on the editor document.
18498      * For visual commands, you should use {@link #relayCmd} instead.
18499      * <b>This should only be called after the editor is initialized.</b>
18500      * @param {String} cmd The Midas command
18501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18502      */
18503     execCmd : function(cmd, value){
18504         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18505         this.syncValue();
18506     },
18507  
18508  
18509    
18510     /**
18511      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18512      * to insert tRoo.
18513      * @param {String} text | dom node.. 
18514      */
18515     insertAtCursor : function(text)
18516     {
18517         
18518         
18519         
18520         if(!this.activated){
18521             return;
18522         }
18523         /*
18524         if(Roo.isIE){
18525             this.win.focus();
18526             var r = this.doc.selection.createRange();
18527             if(r){
18528                 r.collapse(true);
18529                 r.pasteHTML(text);
18530                 this.syncValue();
18531                 this.deferFocus();
18532             
18533             }
18534             return;
18535         }
18536         */
18537         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18538             this.win.focus();
18539             
18540             
18541             // from jquery ui (MIT licenced)
18542             var range, node;
18543             var win = this.win;
18544             
18545             if (win.getSelection && win.getSelection().getRangeAt) {
18546                 range = win.getSelection().getRangeAt(0);
18547                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18548                 range.insertNode(node);
18549             } else if (win.document.selection && win.document.selection.createRange) {
18550                 // no firefox support
18551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18552                 win.document.selection.createRange().pasteHTML(txt);
18553             } else {
18554                 // no firefox support
18555                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18556                 this.execCmd('InsertHTML', txt);
18557             } 
18558             
18559             this.syncValue();
18560             
18561             this.deferFocus();
18562         }
18563     },
18564  // private
18565     mozKeyPress : function(e){
18566         if(e.ctrlKey){
18567             var c = e.getCharCode(), cmd;
18568           
18569             if(c > 0){
18570                 c = String.fromCharCode(c).toLowerCase();
18571                 switch(c){
18572                     case 'b':
18573                         cmd = 'bold';
18574                         break;
18575                     case 'i':
18576                         cmd = 'italic';
18577                         break;
18578                     
18579                     case 'u':
18580                         cmd = 'underline';
18581                         break;
18582                     
18583                     case 'v':
18584                         this.cleanUpPaste.defer(100, this);
18585                         return;
18586                         
18587                 }
18588                 if(cmd){
18589                     this.win.focus();
18590                     this.execCmd(cmd);
18591                     this.deferFocus();
18592                     e.preventDefault();
18593                 }
18594                 
18595             }
18596         }
18597     },
18598
18599     // private
18600     fixKeys : function(){ // load time branching for fastest keydown performance
18601         if(Roo.isIE){
18602             return function(e){
18603                 var k = e.getKey(), r;
18604                 if(k == e.TAB){
18605                     e.stopEvent();
18606                     r = this.doc.selection.createRange();
18607                     if(r){
18608                         r.collapse(true);
18609                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18610                         this.deferFocus();
18611                     }
18612                     return;
18613                 }
18614                 
18615                 if(k == e.ENTER){
18616                     r = this.doc.selection.createRange();
18617                     if(r){
18618                         var target = r.parentElement();
18619                         if(!target || target.tagName.toLowerCase() != 'li'){
18620                             e.stopEvent();
18621                             r.pasteHTML('<br />');
18622                             r.collapse(false);
18623                             r.select();
18624                         }
18625                     }
18626                 }
18627                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18628                     this.cleanUpPaste.defer(100, this);
18629                     return;
18630                 }
18631                 
18632                 
18633             };
18634         }else if(Roo.isOpera){
18635             return function(e){
18636                 var k = e.getKey();
18637                 if(k == e.TAB){
18638                     e.stopEvent();
18639                     this.win.focus();
18640                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18641                     this.deferFocus();
18642                 }
18643                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18644                     this.cleanUpPaste.defer(100, this);
18645                     return;
18646                 }
18647                 
18648             };
18649         }else if(Roo.isSafari){
18650             return function(e){
18651                 var k = e.getKey();
18652                 
18653                 if(k == e.TAB){
18654                     e.stopEvent();
18655                     this.execCmd('InsertText','\t');
18656                     this.deferFocus();
18657                     return;
18658                 }
18659                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18660                     this.cleanUpPaste.defer(100, this);
18661                     return;
18662                 }
18663                 
18664              };
18665         }
18666     }(),
18667     
18668     getAllAncestors: function()
18669     {
18670         var p = this.getSelectedNode();
18671         var a = [];
18672         if (!p) {
18673             a.push(p); // push blank onto stack..
18674             p = this.getParentElement();
18675         }
18676         
18677         
18678         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18679             a.push(p);
18680             p = p.parentNode;
18681         }
18682         a.push(this.doc.body);
18683         return a;
18684     },
18685     lastSel : false,
18686     lastSelNode : false,
18687     
18688     
18689     getSelection : function() 
18690     {
18691         this.assignDocWin();
18692         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18693     },
18694     
18695     getSelectedNode: function() 
18696     {
18697         // this may only work on Gecko!!!
18698         
18699         // should we cache this!!!!
18700         
18701         
18702         
18703          
18704         var range = this.createRange(this.getSelection()).cloneRange();
18705         
18706         if (Roo.isIE) {
18707             var parent = range.parentElement();
18708             while (true) {
18709                 var testRange = range.duplicate();
18710                 testRange.moveToElementText(parent);
18711                 if (testRange.inRange(range)) {
18712                     break;
18713                 }
18714                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18715                     break;
18716                 }
18717                 parent = parent.parentElement;
18718             }
18719             return parent;
18720         }
18721         
18722         // is ancestor a text element.
18723         var ac =  range.commonAncestorContainer;
18724         if (ac.nodeType == 3) {
18725             ac = ac.parentNode;
18726         }
18727         
18728         var ar = ac.childNodes;
18729          
18730         var nodes = [];
18731         var other_nodes = [];
18732         var has_other_nodes = false;
18733         for (var i=0;i<ar.length;i++) {
18734             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18735                 continue;
18736             }
18737             // fullly contained node.
18738             
18739             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18740                 nodes.push(ar[i]);
18741                 continue;
18742             }
18743             
18744             // probably selected..
18745             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18746                 other_nodes.push(ar[i]);
18747                 continue;
18748             }
18749             // outer..
18750             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18751                 continue;
18752             }
18753             
18754             
18755             has_other_nodes = true;
18756         }
18757         if (!nodes.length && other_nodes.length) {
18758             nodes= other_nodes;
18759         }
18760         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18761             return false;
18762         }
18763         
18764         return nodes[0];
18765     },
18766     createRange: function(sel)
18767     {
18768         // this has strange effects when using with 
18769         // top toolbar - not sure if it's a great idea.
18770         //this.editor.contentWindow.focus();
18771         if (typeof sel != "undefined") {
18772             try {
18773                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18774             } catch(e) {
18775                 return this.doc.createRange();
18776             }
18777         } else {
18778             return this.doc.createRange();
18779         }
18780     },
18781     getParentElement: function()
18782     {
18783         
18784         this.assignDocWin();
18785         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18786         
18787         var range = this.createRange(sel);
18788          
18789         try {
18790             var p = range.commonAncestorContainer;
18791             while (p.nodeType == 3) { // text node
18792                 p = p.parentNode;
18793             }
18794             return p;
18795         } catch (e) {
18796             return null;
18797         }
18798     
18799     },
18800     /***
18801      *
18802      * Range intersection.. the hard stuff...
18803      *  '-1' = before
18804      *  '0' = hits..
18805      *  '1' = after.
18806      *         [ -- selected range --- ]
18807      *   [fail]                        [fail]
18808      *
18809      *    basically..
18810      *      if end is before start or  hits it. fail.
18811      *      if start is after end or hits it fail.
18812      *
18813      *   if either hits (but other is outside. - then it's not 
18814      *   
18815      *    
18816      **/
18817     
18818     
18819     // @see http://www.thismuchiknow.co.uk/?p=64.
18820     rangeIntersectsNode : function(range, node)
18821     {
18822         var nodeRange = node.ownerDocument.createRange();
18823         try {
18824             nodeRange.selectNode(node);
18825         } catch (e) {
18826             nodeRange.selectNodeContents(node);
18827         }
18828     
18829         var rangeStartRange = range.cloneRange();
18830         rangeStartRange.collapse(true);
18831     
18832         var rangeEndRange = range.cloneRange();
18833         rangeEndRange.collapse(false);
18834     
18835         var nodeStartRange = nodeRange.cloneRange();
18836         nodeStartRange.collapse(true);
18837     
18838         var nodeEndRange = nodeRange.cloneRange();
18839         nodeEndRange.collapse(false);
18840     
18841         return rangeStartRange.compareBoundaryPoints(
18842                  Range.START_TO_START, nodeEndRange) == -1 &&
18843                rangeEndRange.compareBoundaryPoints(
18844                  Range.START_TO_START, nodeStartRange) == 1;
18845         
18846          
18847     },
18848     rangeCompareNode : function(range, node)
18849     {
18850         var nodeRange = node.ownerDocument.createRange();
18851         try {
18852             nodeRange.selectNode(node);
18853         } catch (e) {
18854             nodeRange.selectNodeContents(node);
18855         }
18856         
18857         
18858         range.collapse(true);
18859     
18860         nodeRange.collapse(true);
18861      
18862         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18863         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18864          
18865         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18866         
18867         var nodeIsBefore   =  ss == 1;
18868         var nodeIsAfter    = ee == -1;
18869         
18870         if (nodeIsBefore && nodeIsAfter)
18871             return 0; // outer
18872         if (!nodeIsBefore && nodeIsAfter)
18873             return 1; //right trailed.
18874         
18875         if (nodeIsBefore && !nodeIsAfter)
18876             return 2;  // left trailed.
18877         // fully contined.
18878         return 3;
18879     },
18880
18881     // private? - in a new class?
18882     cleanUpPaste :  function()
18883     {
18884         // cleans up the whole document..
18885         Roo.log('cleanuppaste');
18886         
18887         this.cleanUpChildren(this.doc.body);
18888         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18889         if (clean != this.doc.body.innerHTML) {
18890             this.doc.body.innerHTML = clean;
18891         }
18892         
18893     },
18894     
18895     cleanWordChars : function(input) {// change the chars to hex code
18896         var he = Roo.HtmlEditorCore;
18897         
18898         var output = input;
18899         Roo.each(he.swapCodes, function(sw) { 
18900             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18901             
18902             output = output.replace(swapper, sw[1]);
18903         });
18904         
18905         return output;
18906     },
18907     
18908     
18909     cleanUpChildren : function (n)
18910     {
18911         if (!n.childNodes.length) {
18912             return;
18913         }
18914         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18915            this.cleanUpChild(n.childNodes[i]);
18916         }
18917     },
18918     
18919     
18920         
18921     
18922     cleanUpChild : function (node)
18923     {
18924         var ed = this;
18925         //console.log(node);
18926         if (node.nodeName == "#text") {
18927             // clean up silly Windows -- stuff?
18928             return; 
18929         }
18930         if (node.nodeName == "#comment") {
18931             node.parentNode.removeChild(node);
18932             // clean up silly Windows -- stuff?
18933             return; 
18934         }
18935         var lcname = node.tagName.toLowerCase();
18936         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18937         // whitelist of tags..
18938         
18939         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18940             // remove node.
18941             node.parentNode.removeChild(node);
18942             return;
18943             
18944         }
18945         
18946         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18947         
18948         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18949         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18950         
18951         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18952         //    remove_keep_children = true;
18953         //}
18954         
18955         if (remove_keep_children) {
18956             this.cleanUpChildren(node);
18957             // inserts everything just before this node...
18958             while (node.childNodes.length) {
18959                 var cn = node.childNodes[0];
18960                 node.removeChild(cn);
18961                 node.parentNode.insertBefore(cn, node);
18962             }
18963             node.parentNode.removeChild(node);
18964             return;
18965         }
18966         
18967         if (!node.attributes || !node.attributes.length) {
18968             this.cleanUpChildren(node);
18969             return;
18970         }
18971         
18972         function cleanAttr(n,v)
18973         {
18974             
18975             if (v.match(/^\./) || v.match(/^\//)) {
18976                 return;
18977             }
18978             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18979                 return;
18980             }
18981             if (v.match(/^#/)) {
18982                 return;
18983             }
18984 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18985             node.removeAttribute(n);
18986             
18987         }
18988         
18989         var cwhite = this.cwhite;
18990         var cblack = this.cblack;
18991             
18992         function cleanStyle(n,v)
18993         {
18994             if (v.match(/expression/)) { //XSS?? should we even bother..
18995                 node.removeAttribute(n);
18996                 return;
18997             }
18998             
18999             var parts = v.split(/;/);
19000             var clean = [];
19001             
19002             Roo.each(parts, function(p) {
19003                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19004                 if (!p.length) {
19005                     return true;
19006                 }
19007                 var l = p.split(':').shift().replace(/\s+/g,'');
19008                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19009                 
19010                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19011 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19012                     //node.removeAttribute(n);
19013                     return true;
19014                 }
19015                 //Roo.log()
19016                 // only allow 'c whitelisted system attributes'
19017                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19018 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19019                     //node.removeAttribute(n);
19020                     return true;
19021                 }
19022                 
19023                 
19024                  
19025                 
19026                 clean.push(p);
19027                 return true;
19028             });
19029             if (clean.length) { 
19030                 node.setAttribute(n, clean.join(';'));
19031             } else {
19032                 node.removeAttribute(n);
19033             }
19034             
19035         }
19036         
19037         
19038         for (var i = node.attributes.length-1; i > -1 ; i--) {
19039             var a = node.attributes[i];
19040             //console.log(a);
19041             
19042             if (a.name.toLowerCase().substr(0,2)=='on')  {
19043                 node.removeAttribute(a.name);
19044                 continue;
19045             }
19046             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19047                 node.removeAttribute(a.name);
19048                 continue;
19049             }
19050             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19051                 cleanAttr(a.name,a.value); // fixme..
19052                 continue;
19053             }
19054             if (a.name == 'style') {
19055                 cleanStyle(a.name,a.value);
19056                 continue;
19057             }
19058             /// clean up MS crap..
19059             // tecnically this should be a list of valid class'es..
19060             
19061             
19062             if (a.name == 'class') {
19063                 if (a.value.match(/^Mso/)) {
19064                     node.className = '';
19065                 }
19066                 
19067                 if (a.value.match(/body/)) {
19068                     node.className = '';
19069                 }
19070                 continue;
19071             }
19072             
19073             // style cleanup!?
19074             // class cleanup?
19075             
19076         }
19077         
19078         
19079         this.cleanUpChildren(node);
19080         
19081         
19082     },
19083     /**
19084      * Clean up MS wordisms...
19085      */
19086     cleanWord : function(node)
19087     {
19088         var _t = this;
19089         var cleanWordChildren = function()
19090         {
19091             if (!node.childNodes.length) {
19092                 return;
19093             }
19094             for (var i = node.childNodes.length-1; i > -1 ; i--) {
19095                _t.cleanWord(node.childNodes[i]);
19096             }
19097         }
19098         
19099         
19100         if (!node) {
19101             this.cleanWord(this.doc.body);
19102             return;
19103         }
19104         if (node.nodeName == "#text") {
19105             // clean up silly Windows -- stuff?
19106             return; 
19107         }
19108         if (node.nodeName == "#comment") {
19109             node.parentNode.removeChild(node);
19110             // clean up silly Windows -- stuff?
19111             return; 
19112         }
19113         
19114         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19115             node.parentNode.removeChild(node);
19116             return;
19117         }
19118         
19119         // remove - but keep children..
19120         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19121             while (node.childNodes.length) {
19122                 var cn = node.childNodes[0];
19123                 node.removeChild(cn);
19124                 node.parentNode.insertBefore(cn, node);
19125             }
19126             node.parentNode.removeChild(node);
19127             cleanWordChildren();
19128             return;
19129         }
19130         // clean styles
19131         if (node.className.length) {
19132             
19133             var cn = node.className.split(/\W+/);
19134             var cna = [];
19135             Roo.each(cn, function(cls) {
19136                 if (cls.match(/Mso[a-zA-Z]+/)) {
19137                     return;
19138                 }
19139                 cna.push(cls);
19140             });
19141             node.className = cna.length ? cna.join(' ') : '';
19142             if (!cna.length) {
19143                 node.removeAttribute("class");
19144             }
19145         }
19146         
19147         if (node.hasAttribute("lang")) {
19148             node.removeAttribute("lang");
19149         }
19150         
19151         if (node.hasAttribute("style")) {
19152             
19153             var styles = node.getAttribute("style").split(";");
19154             var nstyle = [];
19155             Roo.each(styles, function(s) {
19156                 if (!s.match(/:/)) {
19157                     return;
19158                 }
19159                 var kv = s.split(":");
19160                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19161                     return;
19162                 }
19163                 // what ever is left... we allow.
19164                 nstyle.push(s);
19165             });
19166             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19167             if (!nstyle.length) {
19168                 node.removeAttribute('style');
19169             }
19170         }
19171         
19172         cleanWordChildren();
19173         
19174         
19175     },
19176     domToHTML : function(currentElement, depth, nopadtext) {
19177         
19178         depth = depth || 0;
19179         nopadtext = nopadtext || false;
19180     
19181         if (!currentElement) {
19182             return this.domToHTML(this.doc.body);
19183         }
19184         
19185         //Roo.log(currentElement);
19186         var j;
19187         var allText = false;
19188         var nodeName = currentElement.nodeName;
19189         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19190         
19191         if  (nodeName == '#text') {
19192             
19193             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19194         }
19195         
19196         
19197         var ret = '';
19198         if (nodeName != 'BODY') {
19199              
19200             var i = 0;
19201             // Prints the node tagName, such as <A>, <IMG>, etc
19202             if (tagName) {
19203                 var attr = [];
19204                 for(i = 0; i < currentElement.attributes.length;i++) {
19205                     // quoting?
19206                     var aname = currentElement.attributes.item(i).name;
19207                     if (!currentElement.attributes.item(i).value.length) {
19208                         continue;
19209                     }
19210                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19211                 }
19212                 
19213                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19214             } 
19215             else {
19216                 
19217                 // eack
19218             }
19219         } else {
19220             tagName = false;
19221         }
19222         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19223             return ret;
19224         }
19225         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19226             nopadtext = true;
19227         }
19228         
19229         
19230         // Traverse the tree
19231         i = 0;
19232         var currentElementChild = currentElement.childNodes.item(i);
19233         var allText = true;
19234         var innerHTML  = '';
19235         lastnode = '';
19236         while (currentElementChild) {
19237             // Formatting code (indent the tree so it looks nice on the screen)
19238             var nopad = nopadtext;
19239             if (lastnode == 'SPAN') {
19240                 nopad  = true;
19241             }
19242             // text
19243             if  (currentElementChild.nodeName == '#text') {
19244                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19245                 toadd = nopadtext ? toadd : toadd.trim();
19246                 if (!nopad && toadd.length > 80) {
19247                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19248                 }
19249                 innerHTML  += toadd;
19250                 
19251                 i++;
19252                 currentElementChild = currentElement.childNodes.item(i);
19253                 lastNode = '';
19254                 continue;
19255             }
19256             allText = false;
19257             
19258             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19259                 
19260             // Recursively traverse the tree structure of the child node
19261             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19262             lastnode = currentElementChild.nodeName;
19263             i++;
19264             currentElementChild=currentElement.childNodes.item(i);
19265         }
19266         
19267         ret += innerHTML;
19268         
19269         if (!allText) {
19270                 // The remaining code is mostly for formatting the tree
19271             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19272         }
19273         
19274         
19275         if (tagName) {
19276             ret+= "</"+tagName+">";
19277         }
19278         return ret;
19279         
19280     },
19281         
19282     applyBlacklists : function()
19283     {
19284         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19285         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19286         
19287         this.white = [];
19288         this.black = [];
19289         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19290             if (b.indexOf(tag) > -1) {
19291                 return;
19292             }
19293             this.white.push(tag);
19294             
19295         }, this);
19296         
19297         Roo.each(w, function(tag) {
19298             if (b.indexOf(tag) > -1) {
19299                 return;
19300             }
19301             if (this.white.indexOf(tag) > -1) {
19302                 return;
19303             }
19304             this.white.push(tag);
19305             
19306         }, this);
19307         
19308         
19309         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19310             if (w.indexOf(tag) > -1) {
19311                 return;
19312             }
19313             this.black.push(tag);
19314             
19315         }, this);
19316         
19317         Roo.each(b, function(tag) {
19318             if (w.indexOf(tag) > -1) {
19319                 return;
19320             }
19321             if (this.black.indexOf(tag) > -1) {
19322                 return;
19323             }
19324             this.black.push(tag);
19325             
19326         }, this);
19327         
19328         
19329         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19330         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19331         
19332         this.cwhite = [];
19333         this.cblack = [];
19334         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19335             if (b.indexOf(tag) > -1) {
19336                 return;
19337             }
19338             this.cwhite.push(tag);
19339             
19340         }, this);
19341         
19342         Roo.each(w, function(tag) {
19343             if (b.indexOf(tag) > -1) {
19344                 return;
19345             }
19346             if (this.cwhite.indexOf(tag) > -1) {
19347                 return;
19348             }
19349             this.cwhite.push(tag);
19350             
19351         }, this);
19352         
19353         
19354         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19355             if (w.indexOf(tag) > -1) {
19356                 return;
19357             }
19358             this.cblack.push(tag);
19359             
19360         }, this);
19361         
19362         Roo.each(b, function(tag) {
19363             if (w.indexOf(tag) > -1) {
19364                 return;
19365             }
19366             if (this.cblack.indexOf(tag) > -1) {
19367                 return;
19368             }
19369             this.cblack.push(tag);
19370             
19371         }, this);
19372     },
19373     
19374     setStylesheets : function(stylesheets)
19375     {
19376         if(typeof(stylesheets) == 'string'){
19377             Roo.get(this.iframe.contentDocument.head).createChild({
19378                 tag : 'link',
19379                 rel : 'stylesheet',
19380                 type : 'text/css',
19381                 href : stylesheets
19382             });
19383             
19384             return;
19385         }
19386         var _this = this;
19387      
19388         Roo.each(stylesheets, function(s) {
19389             if(!s.length){
19390                 return;
19391             }
19392             
19393             Roo.get(_this.iframe.contentDocument.head).createChild({
19394                 tag : 'link',
19395                 rel : 'stylesheet',
19396                 type : 'text/css',
19397                 href : s
19398             });
19399         });
19400
19401         
19402     },
19403     
19404     removeStylesheets : function()
19405     {
19406         var _this = this;
19407         
19408         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19409             s.remove();
19410         });
19411     }
19412     
19413     // hide stuff that is not compatible
19414     /**
19415      * @event blur
19416      * @hide
19417      */
19418     /**
19419      * @event change
19420      * @hide
19421      */
19422     /**
19423      * @event focus
19424      * @hide
19425      */
19426     /**
19427      * @event specialkey
19428      * @hide
19429      */
19430     /**
19431      * @cfg {String} fieldClass @hide
19432      */
19433     /**
19434      * @cfg {String} focusClass @hide
19435      */
19436     /**
19437      * @cfg {String} autoCreate @hide
19438      */
19439     /**
19440      * @cfg {String} inputType @hide
19441      */
19442     /**
19443      * @cfg {String} invalidClass @hide
19444      */
19445     /**
19446      * @cfg {String} invalidText @hide
19447      */
19448     /**
19449      * @cfg {String} msgFx @hide
19450      */
19451     /**
19452      * @cfg {String} validateOnBlur @hide
19453      */
19454 });
19455
19456 Roo.HtmlEditorCore.white = [
19457         'area', 'br', 'img', 'input', 'hr', 'wbr',
19458         
19459        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19460        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19461        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19462        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19463        'table',   'ul',         'xmp', 
19464        
19465        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19466       'thead',   'tr', 
19467      
19468       'dir', 'menu', 'ol', 'ul', 'dl',
19469        
19470       'embed',  'object'
19471 ];
19472
19473
19474 Roo.HtmlEditorCore.black = [
19475     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19476         'applet', // 
19477         'base',   'basefont', 'bgsound', 'blink',  'body', 
19478         'frame',  'frameset', 'head',    'html',   'ilayer', 
19479         'iframe', 'layer',  'link',     'meta',    'object',   
19480         'script', 'style' ,'title',  'xml' // clean later..
19481 ];
19482 Roo.HtmlEditorCore.clean = [
19483     'script', 'style', 'title', 'xml'
19484 ];
19485 Roo.HtmlEditorCore.remove = [
19486     'font'
19487 ];
19488 // attributes..
19489
19490 Roo.HtmlEditorCore.ablack = [
19491     'on'
19492 ];
19493     
19494 Roo.HtmlEditorCore.aclean = [ 
19495     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19496 ];
19497
19498 // protocols..
19499 Roo.HtmlEditorCore.pwhite= [
19500         'http',  'https',  'mailto'
19501 ];
19502
19503 // white listed style attributes.
19504 Roo.HtmlEditorCore.cwhite= [
19505       //  'text-align', /// default is to allow most things..
19506       
19507          
19508 //        'font-size'//??
19509 ];
19510
19511 // black listed style attributes.
19512 Roo.HtmlEditorCore.cblack= [
19513       //  'font-size' -- this can be set by the project 
19514 ];
19515
19516
19517 Roo.HtmlEditorCore.swapCodes   =[ 
19518     [    8211, "--" ], 
19519     [    8212, "--" ], 
19520     [    8216,  "'" ],  
19521     [    8217, "'" ],  
19522     [    8220, '"' ],  
19523     [    8221, '"' ],  
19524     [    8226, "*" ],  
19525     [    8230, "..." ]
19526 ]; 
19527
19528     /*
19529  * - LGPL
19530  *
19531  * HtmlEditor
19532  * 
19533  */
19534
19535 /**
19536  * @class Roo.bootstrap.HtmlEditor
19537  * @extends Roo.bootstrap.TextArea
19538  * Bootstrap HtmlEditor class
19539
19540  * @constructor
19541  * Create a new HtmlEditor
19542  * @param {Object} config The config object
19543  */
19544
19545 Roo.bootstrap.HtmlEditor = function(config){
19546     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19547     if (!this.toolbars) {
19548         this.toolbars = [];
19549     }
19550     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19551     this.addEvents({
19552             /**
19553              * @event initialize
19554              * Fires when the editor is fully initialized (including the iframe)
19555              * @param {HtmlEditor} this
19556              */
19557             initialize: true,
19558             /**
19559              * @event activate
19560              * Fires when the editor is first receives the focus. Any insertion must wait
19561              * until after this event.
19562              * @param {HtmlEditor} this
19563              */
19564             activate: true,
19565              /**
19566              * @event beforesync
19567              * Fires before the textarea is updated with content from the editor iframe. Return false
19568              * to cancel the sync.
19569              * @param {HtmlEditor} this
19570              * @param {String} html
19571              */
19572             beforesync: true,
19573              /**
19574              * @event beforepush
19575              * Fires before the iframe editor is updated with content from the textarea. Return false
19576              * to cancel the push.
19577              * @param {HtmlEditor} this
19578              * @param {String} html
19579              */
19580             beforepush: true,
19581              /**
19582              * @event sync
19583              * Fires when the textarea is updated with content from the editor iframe.
19584              * @param {HtmlEditor} this
19585              * @param {String} html
19586              */
19587             sync: true,
19588              /**
19589              * @event push
19590              * Fires when the iframe editor is updated with content from the textarea.
19591              * @param {HtmlEditor} this
19592              * @param {String} html
19593              */
19594             push: true,
19595              /**
19596              * @event editmodechange
19597              * Fires when the editor switches edit modes
19598              * @param {HtmlEditor} this
19599              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19600              */
19601             editmodechange: true,
19602             /**
19603              * @event editorevent
19604              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19605              * @param {HtmlEditor} this
19606              */
19607             editorevent: true,
19608             /**
19609              * @event firstfocus
19610              * Fires when on first focus - needed by toolbars..
19611              * @param {HtmlEditor} this
19612              */
19613             firstfocus: true,
19614             /**
19615              * @event autosave
19616              * Auto save the htmlEditor value as a file into Events
19617              * @param {HtmlEditor} this
19618              */
19619             autosave: true,
19620             /**
19621              * @event savedpreview
19622              * preview the saved version of htmlEditor
19623              * @param {HtmlEditor} this
19624              */
19625             savedpreview: true
19626         });
19627 };
19628
19629
19630 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19631     
19632     
19633       /**
19634      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19635      */
19636     toolbars : false,
19637    
19638      /**
19639      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19640      *                        Roo.resizable.
19641      */
19642     resizable : false,
19643      /**
19644      * @cfg {Number} height (in pixels)
19645      */   
19646     height: 300,
19647    /**
19648      * @cfg {Number} width (in pixels)
19649      */   
19650     width: false,
19651     
19652     /**
19653      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19654      * 
19655      */
19656     stylesheets: false,
19657     
19658     // id of frame..
19659     frameId: false,
19660     
19661     // private properties
19662     validationEvent : false,
19663     deferHeight: true,
19664     initialized : false,
19665     activated : false,
19666     
19667     onFocus : Roo.emptyFn,
19668     iframePad:3,
19669     hideMode:'offsets',
19670     
19671     
19672     tbContainer : false,
19673     
19674     toolbarContainer :function() {
19675         return this.wrap.select('.x-html-editor-tb',true).first();
19676     },
19677
19678     /**
19679      * Protected method that will not generally be called directly. It
19680      * is called when the editor creates its toolbar. Override this method if you need to
19681      * add custom toolbar buttons.
19682      * @param {HtmlEditor} editor
19683      */
19684     createToolbar : function(){
19685         
19686         Roo.log("create toolbars");
19687         
19688         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19689         this.toolbars[0].render(this.toolbarContainer());
19690         
19691         return;
19692         
19693 //        if (!editor.toolbars || !editor.toolbars.length) {
19694 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19695 //        }
19696 //        
19697 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19698 //            editor.toolbars[i] = Roo.factory(
19699 //                    typeof(editor.toolbars[i]) == 'string' ?
19700 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19701 //                Roo.bootstrap.HtmlEditor);
19702 //            editor.toolbars[i].init(editor);
19703 //        }
19704     },
19705
19706      
19707     // private
19708     onRender : function(ct, position)
19709     {
19710        // Roo.log("Call onRender: " + this.xtype);
19711         var _t = this;
19712         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19713       
19714         this.wrap = this.inputEl().wrap({
19715             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19716         });
19717         
19718         this.editorcore.onRender(ct, position);
19719          
19720         if (this.resizable) {
19721             this.resizeEl = new Roo.Resizable(this.wrap, {
19722                 pinned : true,
19723                 wrap: true,
19724                 dynamic : true,
19725                 minHeight : this.height,
19726                 height: this.height,
19727                 handles : this.resizable,
19728                 width: this.width,
19729                 listeners : {
19730                     resize : function(r, w, h) {
19731                         _t.onResize(w,h); // -something
19732                     }
19733                 }
19734             });
19735             
19736         }
19737         this.createToolbar(this);
19738        
19739         
19740         if(!this.width && this.resizable){
19741             this.setSize(this.wrap.getSize());
19742         }
19743         if (this.resizeEl) {
19744             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19745             // should trigger onReize..
19746         }
19747         
19748     },
19749
19750     // private
19751     onResize : function(w, h)
19752     {
19753         Roo.log('resize: ' +w + ',' + h );
19754         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19755         var ew = false;
19756         var eh = false;
19757         
19758         if(this.inputEl() ){
19759             if(typeof w == 'number'){
19760                 var aw = w - this.wrap.getFrameWidth('lr');
19761                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19762                 ew = aw;
19763             }
19764             if(typeof h == 'number'){
19765                  var tbh = -11;  // fixme it needs to tool bar size!
19766                 for (var i =0; i < this.toolbars.length;i++) {
19767                     // fixme - ask toolbars for heights?
19768                     tbh += this.toolbars[i].el.getHeight();
19769                     //if (this.toolbars[i].footer) {
19770                     //    tbh += this.toolbars[i].footer.el.getHeight();
19771                     //}
19772                 }
19773               
19774                 
19775                 
19776                 
19777                 
19778                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19779                 ah -= 5; // knock a few pixes off for look..
19780                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19781                 var eh = ah;
19782             }
19783         }
19784         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19785         this.editorcore.onResize(ew,eh);
19786         
19787     },
19788
19789     /**
19790      * Toggles the editor between standard and source edit mode.
19791      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19792      */
19793     toggleSourceEdit : function(sourceEditMode)
19794     {
19795         this.editorcore.toggleSourceEdit(sourceEditMode);
19796         
19797         if(this.editorcore.sourceEditMode){
19798             Roo.log('editor - showing textarea');
19799             
19800 //            Roo.log('in');
19801 //            Roo.log(this.syncValue());
19802             this.syncValue();
19803             this.inputEl().removeClass(['hide', 'x-hidden']);
19804             this.inputEl().dom.removeAttribute('tabIndex');
19805             this.inputEl().focus();
19806         }else{
19807             Roo.log('editor - hiding textarea');
19808 //            Roo.log('out')
19809 //            Roo.log(this.pushValue()); 
19810             this.pushValue();
19811             
19812             this.inputEl().addClass(['hide', 'x-hidden']);
19813             this.inputEl().dom.setAttribute('tabIndex', -1);
19814             //this.deferFocus();
19815         }
19816          
19817         if(this.resizable){
19818             this.setSize(this.wrap.getSize());
19819         }
19820         
19821         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19822     },
19823  
19824     // private (for BoxComponent)
19825     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19826
19827     // private (for BoxComponent)
19828     getResizeEl : function(){
19829         return this.wrap;
19830     },
19831
19832     // private (for BoxComponent)
19833     getPositionEl : function(){
19834         return this.wrap;
19835     },
19836
19837     // private
19838     initEvents : function(){
19839         this.originalValue = this.getValue();
19840     },
19841
19842 //    /**
19843 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19844 //     * @method
19845 //     */
19846 //    markInvalid : Roo.emptyFn,
19847 //    /**
19848 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19849 //     * @method
19850 //     */
19851 //    clearInvalid : Roo.emptyFn,
19852
19853     setValue : function(v){
19854         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19855         this.editorcore.pushValue();
19856     },
19857
19858      
19859     // private
19860     deferFocus : function(){
19861         this.focus.defer(10, this);
19862     },
19863
19864     // doc'ed in Field
19865     focus : function(){
19866         this.editorcore.focus();
19867         
19868     },
19869       
19870
19871     // private
19872     onDestroy : function(){
19873         
19874         
19875         
19876         if(this.rendered){
19877             
19878             for (var i =0; i < this.toolbars.length;i++) {
19879                 // fixme - ask toolbars for heights?
19880                 this.toolbars[i].onDestroy();
19881             }
19882             
19883             this.wrap.dom.innerHTML = '';
19884             this.wrap.remove();
19885         }
19886     },
19887
19888     // private
19889     onFirstFocus : function(){
19890         //Roo.log("onFirstFocus");
19891         this.editorcore.onFirstFocus();
19892          for (var i =0; i < this.toolbars.length;i++) {
19893             this.toolbars[i].onFirstFocus();
19894         }
19895         
19896     },
19897     
19898     // private
19899     syncValue : function()
19900     {   
19901         this.editorcore.syncValue();
19902     },
19903     
19904     pushValue : function()
19905     {   
19906         this.editorcore.pushValue();
19907     }
19908      
19909     
19910     // hide stuff that is not compatible
19911     /**
19912      * @event blur
19913      * @hide
19914      */
19915     /**
19916      * @event change
19917      * @hide
19918      */
19919     /**
19920      * @event focus
19921      * @hide
19922      */
19923     /**
19924      * @event specialkey
19925      * @hide
19926      */
19927     /**
19928      * @cfg {String} fieldClass @hide
19929      */
19930     /**
19931      * @cfg {String} focusClass @hide
19932      */
19933     /**
19934      * @cfg {String} autoCreate @hide
19935      */
19936     /**
19937      * @cfg {String} inputType @hide
19938      */
19939     /**
19940      * @cfg {String} invalidClass @hide
19941      */
19942     /**
19943      * @cfg {String} invalidText @hide
19944      */
19945     /**
19946      * @cfg {String} msgFx @hide
19947      */
19948     /**
19949      * @cfg {String} validateOnBlur @hide
19950      */
19951 });
19952  
19953     
19954    
19955    
19956    
19957       
19958 Roo.namespace('Roo.bootstrap.htmleditor');
19959 /**
19960  * @class Roo.bootstrap.HtmlEditorToolbar1
19961  * Basic Toolbar
19962  * 
19963  * Usage:
19964  *
19965  new Roo.bootstrap.HtmlEditor({
19966     ....
19967     toolbars : [
19968         new Roo.bootstrap.HtmlEditorToolbar1({
19969             disable : { fonts: 1 , format: 1, ..., ... , ...],
19970             btns : [ .... ]
19971         })
19972     }
19973      
19974  * 
19975  * @cfg {Object} disable List of elements to disable..
19976  * @cfg {Array} btns List of additional buttons.
19977  * 
19978  * 
19979  * NEEDS Extra CSS? 
19980  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19981  */
19982  
19983 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19984 {
19985     
19986     Roo.apply(this, config);
19987     
19988     // default disabled, based on 'good practice'..
19989     this.disable = this.disable || {};
19990     Roo.applyIf(this.disable, {
19991         fontSize : true,
19992         colors : true,
19993         specialElements : true
19994     });
19995     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19996     
19997     this.editor = config.editor;
19998     this.editorcore = config.editor.editorcore;
19999     
20000     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20001     
20002     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20003     // dont call parent... till later.
20004 }
20005 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20006      
20007     bar : true,
20008     
20009     editor : false,
20010     editorcore : false,
20011     
20012     
20013     formats : [
20014         "p" ,  
20015         "h1","h2","h3","h4","h5","h6", 
20016         "pre", "code", 
20017         "abbr", "acronym", "address", "cite", "samp", "var",
20018         'div','span'
20019     ],
20020     
20021     onRender : function(ct, position)
20022     {
20023        // Roo.log("Call onRender: " + this.xtype);
20024         
20025        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20026        Roo.log(this.el);
20027        this.el.dom.style.marginBottom = '0';
20028        var _this = this;
20029        var editorcore = this.editorcore;
20030        var editor= this.editor;
20031        
20032        var children = [];
20033        var btn = function(id,cmd , toggle, handler){
20034        
20035             var  event = toggle ? 'toggle' : 'click';
20036        
20037             var a = {
20038                 size : 'sm',
20039                 xtype: 'Button',
20040                 xns: Roo.bootstrap,
20041                 glyphicon : id,
20042                 cmd : id || cmd,
20043                 enableToggle:toggle !== false,
20044                 //html : 'submit'
20045                 pressed : toggle ? false : null,
20046                 listeners : {}
20047             }
20048             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20049                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20050             }
20051             children.push(a);
20052             return a;
20053        }
20054         
20055         var style = {
20056                 xtype: 'Button',
20057                 size : 'sm',
20058                 xns: Roo.bootstrap,
20059                 glyphicon : 'font',
20060                 //html : 'submit'
20061                 menu : {
20062                     xtype: 'Menu',
20063                     xns: Roo.bootstrap,
20064                     items:  []
20065                 }
20066         };
20067         Roo.each(this.formats, function(f) {
20068             style.menu.items.push({
20069                 xtype :'MenuItem',
20070                 xns: Roo.bootstrap,
20071                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20072                 tagname : f,
20073                 listeners : {
20074                     click : function()
20075                     {
20076                         editorcore.insertTag(this.tagname);
20077                         editor.focus();
20078                     }
20079                 }
20080                 
20081             });
20082         });
20083          children.push(style);   
20084             
20085             
20086         btn('bold',false,true);
20087         btn('italic',false,true);
20088         btn('align-left', 'justifyleft',true);
20089         btn('align-center', 'justifycenter',true);
20090         btn('align-right' , 'justifyright',true);
20091         btn('link', false, false, function(btn) {
20092             //Roo.log("create link?");
20093             var url = prompt(this.createLinkText, this.defaultLinkValue);
20094             if(url && url != 'http:/'+'/'){
20095                 this.editorcore.relayCmd('createlink', url);
20096             }
20097         }),
20098         btn('list','insertunorderedlist',true);
20099         btn('pencil', false,true, function(btn){
20100                 Roo.log(this);
20101                 
20102                 this.toggleSourceEdit(btn.pressed);
20103         });
20104         /*
20105         var cog = {
20106                 xtype: 'Button',
20107                 size : 'sm',
20108                 xns: Roo.bootstrap,
20109                 glyphicon : 'cog',
20110                 //html : 'submit'
20111                 menu : {
20112                     xtype: 'Menu',
20113                     xns: Roo.bootstrap,
20114                     items:  []
20115                 }
20116         };
20117         
20118         cog.menu.items.push({
20119             xtype :'MenuItem',
20120             xns: Roo.bootstrap,
20121             html : Clean styles,
20122             tagname : f,
20123             listeners : {
20124                 click : function()
20125                 {
20126                     editorcore.insertTag(this.tagname);
20127                     editor.focus();
20128                 }
20129             }
20130             
20131         });
20132        */
20133         
20134          
20135        this.xtype = 'NavSimplebar';
20136         
20137         for(var i=0;i< children.length;i++) {
20138             
20139             this.buttons.add(this.addxtypeChild(children[i]));
20140             
20141         }
20142         
20143         editor.on('editorevent', this.updateToolbar, this);
20144     },
20145     onBtnClick : function(id)
20146     {
20147        this.editorcore.relayCmd(id);
20148        this.editorcore.focus();
20149     },
20150     
20151     /**
20152      * Protected method that will not generally be called directly. It triggers
20153      * a toolbar update by reading the markup state of the current selection in the editor.
20154      */
20155     updateToolbar: function(){
20156
20157         if(!this.editorcore.activated){
20158             this.editor.onFirstFocus(); // is this neeed?
20159             return;
20160         }
20161
20162         var btns = this.buttons; 
20163         var doc = this.editorcore.doc;
20164         btns.get('bold').setActive(doc.queryCommandState('bold'));
20165         btns.get('italic').setActive(doc.queryCommandState('italic'));
20166         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20167         
20168         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20169         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20170         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20171         
20172         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20173         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20174          /*
20175         
20176         var ans = this.editorcore.getAllAncestors();
20177         if (this.formatCombo) {
20178             
20179             
20180             var store = this.formatCombo.store;
20181             this.formatCombo.setValue("");
20182             for (var i =0; i < ans.length;i++) {
20183                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20184                     // select it..
20185                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20186                     break;
20187                 }
20188             }
20189         }
20190         
20191         
20192         
20193         // hides menus... - so this cant be on a menu...
20194         Roo.bootstrap.MenuMgr.hideAll();
20195         */
20196         Roo.bootstrap.MenuMgr.hideAll();
20197         //this.editorsyncValue();
20198     },
20199     onFirstFocus: function() {
20200         this.buttons.each(function(item){
20201            item.enable();
20202         });
20203     },
20204     toggleSourceEdit : function(sourceEditMode){
20205         
20206           
20207         if(sourceEditMode){
20208             Roo.log("disabling buttons");
20209            this.buttons.each( function(item){
20210                 if(item.cmd != 'pencil'){
20211                     item.disable();
20212                 }
20213             });
20214           
20215         }else{
20216             Roo.log("enabling buttons");
20217             if(this.editorcore.initialized){
20218                 this.buttons.each( function(item){
20219                     item.enable();
20220                 });
20221             }
20222             
20223         }
20224         Roo.log("calling toggole on editor");
20225         // tell the editor that it's been pressed..
20226         this.editor.toggleSourceEdit(sourceEditMode);
20227        
20228     }
20229 });
20230
20231
20232
20233
20234
20235 /**
20236  * @class Roo.bootstrap.Table.AbstractSelectionModel
20237  * @extends Roo.util.Observable
20238  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20239  * implemented by descendant classes.  This class should not be directly instantiated.
20240  * @constructor
20241  */
20242 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20243     this.locked = false;
20244     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20245 };
20246
20247
20248 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20249     /** @ignore Called by the grid automatically. Do not call directly. */
20250     init : function(grid){
20251         this.grid = grid;
20252         this.initEvents();
20253     },
20254
20255     /**
20256      * Locks the selections.
20257      */
20258     lock : function(){
20259         this.locked = true;
20260     },
20261
20262     /**
20263      * Unlocks the selections.
20264      */
20265     unlock : function(){
20266         this.locked = false;
20267     },
20268
20269     /**
20270      * Returns true if the selections are locked.
20271      * @return {Boolean}
20272      */
20273     isLocked : function(){
20274         return this.locked;
20275     }
20276 });
20277 /**
20278  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20279  * @class Roo.bootstrap.Table.RowSelectionModel
20280  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20281  * It supports multiple selections and keyboard selection/navigation. 
20282  * @constructor
20283  * @param {Object} config
20284  */
20285
20286 Roo.bootstrap.Table.RowSelectionModel = function(config){
20287     Roo.apply(this, config);
20288     this.selections = new Roo.util.MixedCollection(false, function(o){
20289         return o.id;
20290     });
20291
20292     this.last = false;
20293     this.lastActive = false;
20294
20295     this.addEvents({
20296         /**
20297              * @event selectionchange
20298              * Fires when the selection changes
20299              * @param {SelectionModel} this
20300              */
20301             "selectionchange" : true,
20302         /**
20303              * @event afterselectionchange
20304              * Fires after the selection changes (eg. by key press or clicking)
20305              * @param {SelectionModel} this
20306              */
20307             "afterselectionchange" : true,
20308         /**
20309              * @event beforerowselect
20310              * Fires when a row is selected being selected, return false to cancel.
20311              * @param {SelectionModel} this
20312              * @param {Number} rowIndex The selected index
20313              * @param {Boolean} keepExisting False if other selections will be cleared
20314              */
20315             "beforerowselect" : true,
20316         /**
20317              * @event rowselect
20318              * Fires when a row is selected.
20319              * @param {SelectionModel} this
20320              * @param {Number} rowIndex The selected index
20321              * @param {Roo.data.Record} r The record
20322              */
20323             "rowselect" : true,
20324         /**
20325              * @event rowdeselect
20326              * Fires when a row is deselected.
20327              * @param {SelectionModel} this
20328              * @param {Number} rowIndex The selected index
20329              */
20330         "rowdeselect" : true
20331     });
20332     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20333     this.locked = false;
20334 };
20335
20336 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20337     /**
20338      * @cfg {Boolean} singleSelect
20339      * True to allow selection of only one row at a time (defaults to false)
20340      */
20341     singleSelect : false,
20342
20343     // private
20344     initEvents : function(){
20345
20346         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20347             this.grid.on("mousedown", this.handleMouseDown, this);
20348         }else{ // allow click to work like normal
20349             this.grid.on("rowclick", this.handleDragableRowClick, this);
20350         }
20351
20352         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20353             "up" : function(e){
20354                 if(!e.shiftKey){
20355                     this.selectPrevious(e.shiftKey);
20356                 }else if(this.last !== false && this.lastActive !== false){
20357                     var last = this.last;
20358                     this.selectRange(this.last,  this.lastActive-1);
20359                     this.grid.getView().focusRow(this.lastActive);
20360                     if(last !== false){
20361                         this.last = last;
20362                     }
20363                 }else{
20364                     this.selectFirstRow();
20365                 }
20366                 this.fireEvent("afterselectionchange", this);
20367             },
20368             "down" : function(e){
20369                 if(!e.shiftKey){
20370                     this.selectNext(e.shiftKey);
20371                 }else if(this.last !== false && this.lastActive !== false){
20372                     var last = this.last;
20373                     this.selectRange(this.last,  this.lastActive+1);
20374                     this.grid.getView().focusRow(this.lastActive);
20375                     if(last !== false){
20376                         this.last = last;
20377                     }
20378                 }else{
20379                     this.selectFirstRow();
20380                 }
20381                 this.fireEvent("afterselectionchange", this);
20382             },
20383             scope: this
20384         });
20385
20386         var view = this.grid.view;
20387         view.on("refresh", this.onRefresh, this);
20388         view.on("rowupdated", this.onRowUpdated, this);
20389         view.on("rowremoved", this.onRemove, this);
20390     },
20391
20392     // private
20393     onRefresh : function(){
20394         var ds = this.grid.dataSource, i, v = this.grid.view;
20395         var s = this.selections;
20396         s.each(function(r){
20397             if((i = ds.indexOfId(r.id)) != -1){
20398                 v.onRowSelect(i);
20399             }else{
20400                 s.remove(r);
20401             }
20402         });
20403     },
20404
20405     // private
20406     onRemove : function(v, index, r){
20407         this.selections.remove(r);
20408     },
20409
20410     // private
20411     onRowUpdated : function(v, index, r){
20412         if(this.isSelected(r)){
20413             v.onRowSelect(index);
20414         }
20415     },
20416
20417     /**
20418      * Select records.
20419      * @param {Array} records The records to select
20420      * @param {Boolean} keepExisting (optional) True to keep existing selections
20421      */
20422     selectRecords : function(records, keepExisting){
20423         if(!keepExisting){
20424             this.clearSelections();
20425         }
20426         var ds = this.grid.dataSource;
20427         for(var i = 0, len = records.length; i < len; i++){
20428             this.selectRow(ds.indexOf(records[i]), true);
20429         }
20430     },
20431
20432     /**
20433      * Gets the number of selected rows.
20434      * @return {Number}
20435      */
20436     getCount : function(){
20437         return this.selections.length;
20438     },
20439
20440     /**
20441      * Selects the first row in the grid.
20442      */
20443     selectFirstRow : function(){
20444         this.selectRow(0);
20445     },
20446
20447     /**
20448      * Select the last row.
20449      * @param {Boolean} keepExisting (optional) True to keep existing selections
20450      */
20451     selectLastRow : function(keepExisting){
20452         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20453     },
20454
20455     /**
20456      * Selects the row immediately following the last selected row.
20457      * @param {Boolean} keepExisting (optional) True to keep existing selections
20458      */
20459     selectNext : function(keepExisting){
20460         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20461             this.selectRow(this.last+1, keepExisting);
20462             this.grid.getView().focusRow(this.last);
20463         }
20464     },
20465
20466     /**
20467      * Selects the row that precedes the last selected row.
20468      * @param {Boolean} keepExisting (optional) True to keep existing selections
20469      */
20470     selectPrevious : function(keepExisting){
20471         if(this.last){
20472             this.selectRow(this.last-1, keepExisting);
20473             this.grid.getView().focusRow(this.last);
20474         }
20475     },
20476
20477     /**
20478      * Returns the selected records
20479      * @return {Array} Array of selected records
20480      */
20481     getSelections : function(){
20482         return [].concat(this.selections.items);
20483     },
20484
20485     /**
20486      * Returns the first selected record.
20487      * @return {Record}
20488      */
20489     getSelected : function(){
20490         return this.selections.itemAt(0);
20491     },
20492
20493
20494     /**
20495      * Clears all selections.
20496      */
20497     clearSelections : function(fast){
20498         if(this.locked) return;
20499         if(fast !== true){
20500             var ds = this.grid.dataSource;
20501             var s = this.selections;
20502             s.each(function(r){
20503                 this.deselectRow(ds.indexOfId(r.id));
20504             }, this);
20505             s.clear();
20506         }else{
20507             this.selections.clear();
20508         }
20509         this.last = false;
20510     },
20511
20512
20513     /**
20514      * Selects all rows.
20515      */
20516     selectAll : function(){
20517         if(this.locked) return;
20518         this.selections.clear();
20519         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20520             this.selectRow(i, true);
20521         }
20522     },
20523
20524     /**
20525      * Returns True if there is a selection.
20526      * @return {Boolean}
20527      */
20528     hasSelection : function(){
20529         return this.selections.length > 0;
20530     },
20531
20532     /**
20533      * Returns True if the specified row is selected.
20534      * @param {Number/Record} record The record or index of the record to check
20535      * @return {Boolean}
20536      */
20537     isSelected : function(index){
20538         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20539         return (r && this.selections.key(r.id) ? true : false);
20540     },
20541
20542     /**
20543      * Returns True if the specified record id is selected.
20544      * @param {String} id The id of record to check
20545      * @return {Boolean}
20546      */
20547     isIdSelected : function(id){
20548         return (this.selections.key(id) ? true : false);
20549     },
20550
20551     // private
20552     handleMouseDown : function(e, t){
20553         var view = this.grid.getView(), rowIndex;
20554         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20555             return;
20556         };
20557         if(e.shiftKey && this.last !== false){
20558             var last = this.last;
20559             this.selectRange(last, rowIndex, e.ctrlKey);
20560             this.last = last; // reset the last
20561             view.focusRow(rowIndex);
20562         }else{
20563             var isSelected = this.isSelected(rowIndex);
20564             if(e.button !== 0 && isSelected){
20565                 view.focusRow(rowIndex);
20566             }else if(e.ctrlKey && isSelected){
20567                 this.deselectRow(rowIndex);
20568             }else if(!isSelected){
20569                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20570                 view.focusRow(rowIndex);
20571             }
20572         }
20573         this.fireEvent("afterselectionchange", this);
20574     },
20575     // private
20576     handleDragableRowClick :  function(grid, rowIndex, e) 
20577     {
20578         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20579             this.selectRow(rowIndex, false);
20580             grid.view.focusRow(rowIndex);
20581              this.fireEvent("afterselectionchange", this);
20582         }
20583     },
20584     
20585     /**
20586      * Selects multiple rows.
20587      * @param {Array} rows Array of the indexes of the row to select
20588      * @param {Boolean} keepExisting (optional) True to keep existing selections
20589      */
20590     selectRows : function(rows, keepExisting){
20591         if(!keepExisting){
20592             this.clearSelections();
20593         }
20594         for(var i = 0, len = rows.length; i < len; i++){
20595             this.selectRow(rows[i], true);
20596         }
20597     },
20598
20599     /**
20600      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20601      * @param {Number} startRow The index of the first row in the range
20602      * @param {Number} endRow The index of the last row in the range
20603      * @param {Boolean} keepExisting (optional) True to retain existing selections
20604      */
20605     selectRange : function(startRow, endRow, keepExisting){
20606         if(this.locked) return;
20607         if(!keepExisting){
20608             this.clearSelections();
20609         }
20610         if(startRow <= endRow){
20611             for(var i = startRow; i <= endRow; i++){
20612                 this.selectRow(i, true);
20613             }
20614         }else{
20615             for(var i = startRow; i >= endRow; i--){
20616                 this.selectRow(i, true);
20617             }
20618         }
20619     },
20620
20621     /**
20622      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20623      * @param {Number} startRow The index of the first row in the range
20624      * @param {Number} endRow The index of the last row in the range
20625      */
20626     deselectRange : function(startRow, endRow, preventViewNotify){
20627         if(this.locked) return;
20628         for(var i = startRow; i <= endRow; i++){
20629             this.deselectRow(i, preventViewNotify);
20630         }
20631     },
20632
20633     /**
20634      * Selects a row.
20635      * @param {Number} row The index of the row to select
20636      * @param {Boolean} keepExisting (optional) True to keep existing selections
20637      */
20638     selectRow : function(index, keepExisting, preventViewNotify){
20639         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20640         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20641             if(!keepExisting || this.singleSelect){
20642                 this.clearSelections();
20643             }
20644             var r = this.grid.dataSource.getAt(index);
20645             this.selections.add(r);
20646             this.last = this.lastActive = index;
20647             if(!preventViewNotify){
20648                 this.grid.getView().onRowSelect(index);
20649             }
20650             this.fireEvent("rowselect", this, index, r);
20651             this.fireEvent("selectionchange", this);
20652         }
20653     },
20654
20655     /**
20656      * Deselects a row.
20657      * @param {Number} row The index of the row to deselect
20658      */
20659     deselectRow : function(index, preventViewNotify){
20660         if(this.locked) return;
20661         if(this.last == index){
20662             this.last = false;
20663         }
20664         if(this.lastActive == index){
20665             this.lastActive = false;
20666         }
20667         var r = this.grid.dataSource.getAt(index);
20668         this.selections.remove(r);
20669         if(!preventViewNotify){
20670             this.grid.getView().onRowDeselect(index);
20671         }
20672         this.fireEvent("rowdeselect", this, index);
20673         this.fireEvent("selectionchange", this);
20674     },
20675
20676     // private
20677     restoreLast : function(){
20678         if(this._last){
20679             this.last = this._last;
20680         }
20681     },
20682
20683     // private
20684     acceptsNav : function(row, col, cm){
20685         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20686     },
20687
20688     // private
20689     onEditorKey : function(field, e){
20690         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20691         if(k == e.TAB){
20692             e.stopEvent();
20693             ed.completeEdit();
20694             if(e.shiftKey){
20695                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20696             }else{
20697                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20698             }
20699         }else if(k == e.ENTER && !e.ctrlKey){
20700             e.stopEvent();
20701             ed.completeEdit();
20702             if(e.shiftKey){
20703                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20704             }else{
20705                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20706             }
20707         }else if(k == e.ESC){
20708             ed.cancelEdit();
20709         }
20710         if(newCell){
20711             g.startEditing(newCell[0], newCell[1]);
20712         }
20713     }
20714 });/*
20715  * Based on:
20716  * Ext JS Library 1.1.1
20717  * Copyright(c) 2006-2007, Ext JS, LLC.
20718  *
20719  * Originally Released Under LGPL - original licence link has changed is not relivant.
20720  *
20721  * Fork - LGPL
20722  * <script type="text/javascript">
20723  */
20724  
20725 /**
20726  * @class Roo.bootstrap.PagingToolbar
20727  * @extends Roo.Row
20728  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20729  * @constructor
20730  * Create a new PagingToolbar
20731  * @param {Object} config The config object
20732  */
20733 Roo.bootstrap.PagingToolbar = function(config)
20734 {
20735     // old args format still supported... - xtype is prefered..
20736         // created from xtype...
20737     var ds = config.dataSource;
20738     this.toolbarItems = [];
20739     if (config.items) {
20740         this.toolbarItems = config.items;
20741 //        config.items = [];
20742     }
20743     
20744     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20745     this.ds = ds;
20746     this.cursor = 0;
20747     if (ds) { 
20748         this.bind(ds);
20749     }
20750     
20751     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20752     
20753 };
20754
20755 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20756     /**
20757      * @cfg {Roo.data.Store} dataSource
20758      * The underlying data store providing the paged data
20759      */
20760     /**
20761      * @cfg {String/HTMLElement/Element} container
20762      * container The id or element that will contain the toolbar
20763      */
20764     /**
20765      * @cfg {Boolean} displayInfo
20766      * True to display the displayMsg (defaults to false)
20767      */
20768     /**
20769      * @cfg {Number} pageSize
20770      * The number of records to display per page (defaults to 20)
20771      */
20772     pageSize: 20,
20773     /**
20774      * @cfg {String} displayMsg
20775      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20776      */
20777     displayMsg : 'Displaying {0} - {1} of {2}',
20778     /**
20779      * @cfg {String} emptyMsg
20780      * The message to display when no records are found (defaults to "No data to display")
20781      */
20782     emptyMsg : 'No data to display',
20783     /**
20784      * Customizable piece of the default paging text (defaults to "Page")
20785      * @type String
20786      */
20787     beforePageText : "Page",
20788     /**
20789      * Customizable piece of the default paging text (defaults to "of %0")
20790      * @type String
20791      */
20792     afterPageText : "of {0}",
20793     /**
20794      * Customizable piece of the default paging text (defaults to "First Page")
20795      * @type String
20796      */
20797     firstText : "First Page",
20798     /**
20799      * Customizable piece of the default paging text (defaults to "Previous Page")
20800      * @type String
20801      */
20802     prevText : "Previous Page",
20803     /**
20804      * Customizable piece of the default paging text (defaults to "Next Page")
20805      * @type String
20806      */
20807     nextText : "Next Page",
20808     /**
20809      * Customizable piece of the default paging text (defaults to "Last Page")
20810      * @type String
20811      */
20812     lastText : "Last Page",
20813     /**
20814      * Customizable piece of the default paging text (defaults to "Refresh")
20815      * @type String
20816      */
20817     refreshText : "Refresh",
20818
20819     buttons : false,
20820     // private
20821     onRender : function(ct, position) 
20822     {
20823         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20824         this.navgroup.parentId = this.id;
20825         this.navgroup.onRender(this.el, null);
20826         // add the buttons to the navgroup
20827         
20828         if(this.displayInfo){
20829             Roo.log(this.el.select('ul.navbar-nav',true).first());
20830             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20831             this.displayEl = this.el.select('.x-paging-info', true).first();
20832 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20833 //            this.displayEl = navel.el.select('span',true).first();
20834         }
20835         
20836         var _this = this;
20837         
20838         if(this.buttons){
20839             Roo.each(_this.buttons, function(e){
20840                Roo.factory(e).onRender(_this.el, null);
20841             });
20842         }
20843             
20844         Roo.each(_this.toolbarItems, function(e) {
20845             _this.navgroup.addItem(e);
20846         });
20847         
20848         
20849         this.first = this.navgroup.addItem({
20850             tooltip: this.firstText,
20851             cls: "prev",
20852             icon : 'fa fa-backward',
20853             disabled: true,
20854             preventDefault: true,
20855             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20856         });
20857         
20858         this.prev =  this.navgroup.addItem({
20859             tooltip: this.prevText,
20860             cls: "prev",
20861             icon : 'fa fa-step-backward',
20862             disabled: true,
20863             preventDefault: true,
20864             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20865         });
20866     //this.addSeparator();
20867         
20868         
20869         var field = this.navgroup.addItem( {
20870             tagtype : 'span',
20871             cls : 'x-paging-position',
20872             
20873             html : this.beforePageText  +
20874                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20875                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20876          } ); //?? escaped?
20877         
20878         this.field = field.el.select('input', true).first();
20879         this.field.on("keydown", this.onPagingKeydown, this);
20880         this.field.on("focus", function(){this.dom.select();});
20881     
20882     
20883         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20884         //this.field.setHeight(18);
20885         //this.addSeparator();
20886         this.next = this.navgroup.addItem({
20887             tooltip: this.nextText,
20888             cls: "next",
20889             html : ' <i class="fa fa-step-forward">',
20890             disabled: true,
20891             preventDefault: true,
20892             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20893         });
20894         this.last = this.navgroup.addItem({
20895             tooltip: this.lastText,
20896             icon : 'fa fa-forward',
20897             cls: "next",
20898             disabled: true,
20899             preventDefault: true,
20900             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20901         });
20902     //this.addSeparator();
20903         this.loading = this.navgroup.addItem({
20904             tooltip: this.refreshText,
20905             icon: 'fa fa-refresh',
20906             preventDefault: true,
20907             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20908         });
20909
20910     },
20911
20912     // private
20913     updateInfo : function(){
20914         if(this.displayEl){
20915             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20916             var msg = count == 0 ?
20917                 this.emptyMsg :
20918                 String.format(
20919                     this.displayMsg,
20920                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20921                 );
20922             this.displayEl.update(msg);
20923         }
20924     },
20925
20926     // private
20927     onLoad : function(ds, r, o){
20928        this.cursor = o.params ? o.params.start : 0;
20929        var d = this.getPageData(),
20930             ap = d.activePage,
20931             ps = d.pages;
20932         
20933        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20934        this.field.dom.value = ap;
20935        this.first.setDisabled(ap == 1);
20936        this.prev.setDisabled(ap == 1);
20937        this.next.setDisabled(ap == ps);
20938        this.last.setDisabled(ap == ps);
20939        this.loading.enable();
20940        this.updateInfo();
20941     },
20942
20943     // private
20944     getPageData : function(){
20945         var total = this.ds.getTotalCount();
20946         return {
20947             total : total,
20948             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20949             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20950         };
20951     },
20952
20953     // private
20954     onLoadError : function(){
20955         this.loading.enable();
20956     },
20957
20958     // private
20959     onPagingKeydown : function(e){
20960         var k = e.getKey();
20961         var d = this.getPageData();
20962         if(k == e.RETURN){
20963             var v = this.field.dom.value, pageNum;
20964             if(!v || isNaN(pageNum = parseInt(v, 10))){
20965                 this.field.dom.value = d.activePage;
20966                 return;
20967             }
20968             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20969             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20970             e.stopEvent();
20971         }
20972         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))
20973         {
20974           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20975           this.field.dom.value = pageNum;
20976           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20977           e.stopEvent();
20978         }
20979         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20980         {
20981           var v = this.field.dom.value, pageNum; 
20982           var increment = (e.shiftKey) ? 10 : 1;
20983           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20984             increment *= -1;
20985           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20986             this.field.dom.value = d.activePage;
20987             return;
20988           }
20989           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20990           {
20991             this.field.dom.value = parseInt(v, 10) + increment;
20992             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20993             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20994           }
20995           e.stopEvent();
20996         }
20997     },
20998
20999     // private
21000     beforeLoad : function(){
21001         if(this.loading){
21002             this.loading.disable();
21003         }
21004     },
21005
21006     // private
21007     onClick : function(which){
21008         
21009         var ds = this.ds;
21010         if (!ds) {
21011             return;
21012         }
21013         
21014         switch(which){
21015             case "first":
21016                 ds.load({params:{start: 0, limit: this.pageSize}});
21017             break;
21018             case "prev":
21019                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21020             break;
21021             case "next":
21022                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21023             break;
21024             case "last":
21025                 var total = ds.getTotalCount();
21026                 var extra = total % this.pageSize;
21027                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21028                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21029             break;
21030             case "refresh":
21031                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21032             break;
21033         }
21034     },
21035
21036     /**
21037      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21038      * @param {Roo.data.Store} store The data store to unbind
21039      */
21040     unbind : function(ds){
21041         ds.un("beforeload", this.beforeLoad, this);
21042         ds.un("load", this.onLoad, this);
21043         ds.un("loadexception", this.onLoadError, this);
21044         ds.un("remove", this.updateInfo, this);
21045         ds.un("add", this.updateInfo, this);
21046         this.ds = undefined;
21047     },
21048
21049     /**
21050      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21051      * @param {Roo.data.Store} store The data store to bind
21052      */
21053     bind : function(ds){
21054         ds.on("beforeload", this.beforeLoad, this);
21055         ds.on("load", this.onLoad, this);
21056         ds.on("loadexception", this.onLoadError, this);
21057         ds.on("remove", this.updateInfo, this);
21058         ds.on("add", this.updateInfo, this);
21059         this.ds = ds;
21060     }
21061 });/*
21062  * - LGPL
21063  *
21064  * element
21065  * 
21066  */
21067
21068 /**
21069  * @class Roo.bootstrap.MessageBar
21070  * @extends Roo.bootstrap.Component
21071  * Bootstrap MessageBar class
21072  * @cfg {String} html contents of the MessageBar
21073  * @cfg {String} weight (info | success | warning | danger) default info
21074  * @cfg {String} beforeClass insert the bar before the given class
21075  * @cfg {Boolean} closable (true | false) default false
21076  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21077  * 
21078  * @constructor
21079  * Create a new Element
21080  * @param {Object} config The config object
21081  */
21082
21083 Roo.bootstrap.MessageBar = function(config){
21084     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21085 };
21086
21087 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21088     
21089     html: '',
21090     weight: 'info',
21091     closable: false,
21092     fixed: false,
21093     beforeClass: 'bootstrap-sticky-wrap',
21094     
21095     getAutoCreate : function(){
21096         
21097         var cfg = {
21098             tag: 'div',
21099             cls: 'alert alert-dismissable alert-' + this.weight,
21100             cn: [
21101                 {
21102                     tag: 'span',
21103                     cls: 'message',
21104                     html: this.html || ''
21105                 }
21106             ]
21107         }
21108         
21109         if(this.fixed){
21110             cfg.cls += ' alert-messages-fixed';
21111         }
21112         
21113         if(this.closable){
21114             cfg.cn.push({
21115                 tag: 'button',
21116                 cls: 'close',
21117                 html: 'x'
21118             });
21119         }
21120         
21121         return cfg;
21122     },
21123     
21124     onRender : function(ct, position)
21125     {
21126         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21127         
21128         if(!this.el){
21129             var cfg = Roo.apply({},  this.getAutoCreate());
21130             cfg.id = Roo.id();
21131             
21132             if (this.cls) {
21133                 cfg.cls += ' ' + this.cls;
21134             }
21135             if (this.style) {
21136                 cfg.style = this.style;
21137             }
21138             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21139             
21140             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21141         }
21142         
21143         this.el.select('>button.close').on('click', this.hide, this);
21144         
21145     },
21146     
21147     show : function()
21148     {
21149         if (!this.rendered) {
21150             this.render();
21151         }
21152         
21153         this.el.show();
21154         
21155         this.fireEvent('show', this);
21156         
21157     },
21158     
21159     hide : function()
21160     {
21161         if (!this.rendered) {
21162             this.render();
21163         }
21164         
21165         this.el.hide();
21166         
21167         this.fireEvent('hide', this);
21168     },
21169     
21170     update : function()
21171     {
21172 //        var e = this.el.dom.firstChild;
21173 //        
21174 //        if(this.closable){
21175 //            e = e.nextSibling;
21176 //        }
21177 //        
21178 //        e.data = this.html || '';
21179
21180         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21181     }
21182    
21183 });
21184
21185  
21186
21187      /*
21188  * - LGPL
21189  *
21190  * Graph
21191  * 
21192  */
21193
21194
21195 /**
21196  * @class Roo.bootstrap.Graph
21197  * @extends Roo.bootstrap.Component
21198  * Bootstrap Graph class
21199 > Prameters
21200  -sm {number} sm 4
21201  -md {number} md 5
21202  @cfg {String} graphtype  bar | vbar | pie
21203  @cfg {number} g_x coodinator | centre x (pie)
21204  @cfg {number} g_y coodinator | centre y (pie)
21205  @cfg {number} g_r radius (pie)
21206  @cfg {number} g_height height of the chart (respected by all elements in the set)
21207  @cfg {number} g_width width of the chart (respected by all elements in the set)
21208  @cfg {Object} title The title of the chart
21209     
21210  -{Array}  values
21211  -opts (object) options for the chart 
21212      o {
21213      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21214      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21215      o vgutter (number)
21216      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.
21217      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21218      o to
21219      o stretch (boolean)
21220      o }
21221  -opts (object) options for the pie
21222      o{
21223      o cut
21224      o startAngle (number)
21225      o endAngle (number)
21226      } 
21227  *
21228  * @constructor
21229  * Create a new Input
21230  * @param {Object} config The config object
21231  */
21232
21233 Roo.bootstrap.Graph = function(config){
21234     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21235     
21236     this.addEvents({
21237         // img events
21238         /**
21239          * @event click
21240          * The img click event for the img.
21241          * @param {Roo.EventObject} e
21242          */
21243         "click" : true
21244     });
21245 };
21246
21247 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21248     
21249     sm: 4,
21250     md: 5,
21251     graphtype: 'bar',
21252     g_height: 250,
21253     g_width: 400,
21254     g_x: 50,
21255     g_y: 50,
21256     g_r: 30,
21257     opts:{
21258         //g_colors: this.colors,
21259         g_type: 'soft',
21260         g_gutter: '20%'
21261
21262     },
21263     title : false,
21264
21265     getAutoCreate : function(){
21266         
21267         var cfg = {
21268             tag: 'div',
21269             html : null
21270         }
21271         
21272         
21273         return  cfg;
21274     },
21275
21276     onRender : function(ct,position){
21277         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21278         this.raphael = Raphael(this.el.dom);
21279         
21280                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21281                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21282                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21283                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21284                 /*
21285                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21286                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21287                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21288                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21289                 
21290                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21291                 r.barchart(330, 10, 300, 220, data1);
21292                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21293                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21294                 */
21295                 
21296                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21297                 // r.barchart(30, 30, 560, 250,  xdata, {
21298                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21299                 //     axis : "0 0 1 1",
21300                 //     axisxlabels :  xdata
21301                 //     //yvalues : cols,
21302                    
21303                 // });
21304 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21305 //        
21306 //        this.load(null,xdata,{
21307 //                axis : "0 0 1 1",
21308 //                axisxlabels :  xdata
21309 //                });
21310
21311     },
21312
21313     load : function(graphtype,xdata,opts){
21314         this.raphael.clear();
21315         if(!graphtype) {
21316             graphtype = this.graphtype;
21317         }
21318         if(!opts){
21319             opts = this.opts;
21320         }
21321         var r = this.raphael,
21322             fin = function () {
21323                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21324             },
21325             fout = function () {
21326                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21327             },
21328             pfin = function() {
21329                 this.sector.stop();
21330                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21331
21332                 if (this.label) {
21333                     this.label[0].stop();
21334                     this.label[0].attr({ r: 7.5 });
21335                     this.label[1].attr({ "font-weight": 800 });
21336                 }
21337             },
21338             pfout = function() {
21339                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21340
21341                 if (this.label) {
21342                     this.label[0].animate({ r: 5 }, 500, "bounce");
21343                     this.label[1].attr({ "font-weight": 400 });
21344                 }
21345             };
21346
21347         switch(graphtype){
21348             case 'bar':
21349                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21350                 break;
21351             case 'hbar':
21352                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21353                 break;
21354             case 'pie':
21355 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21356 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21357 //            
21358                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21359                 
21360                 break;
21361
21362         }
21363         
21364         if(this.title){
21365             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21366         }
21367         
21368     },
21369     
21370     setTitle: function(o)
21371     {
21372         this.title = o;
21373     },
21374     
21375     initEvents: function() {
21376         
21377         if(!this.href){
21378             this.el.on('click', this.onClick, this);
21379         }
21380     },
21381     
21382     onClick : function(e)
21383     {
21384         Roo.log('img onclick');
21385         this.fireEvent('click', this, e);
21386     }
21387    
21388 });
21389
21390  
21391 /*
21392  * - LGPL
21393  *
21394  * numberBox
21395  * 
21396  */
21397 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21398
21399 /**
21400  * @class Roo.bootstrap.dash.NumberBox
21401  * @extends Roo.bootstrap.Component
21402  * Bootstrap NumberBox class
21403  * @cfg {String} headline Box headline
21404  * @cfg {String} content Box content
21405  * @cfg {String} icon Box icon
21406  * @cfg {String} footer Footer text
21407  * @cfg {String} fhref Footer href
21408  * 
21409  * @constructor
21410  * Create a new NumberBox
21411  * @param {Object} config The config object
21412  */
21413
21414
21415 Roo.bootstrap.dash.NumberBox = function(config){
21416     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21417     
21418 };
21419
21420 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21421     
21422     headline : '',
21423     content : '',
21424     icon : '',
21425     footer : '',
21426     fhref : '',
21427     ficon : '',
21428     
21429     getAutoCreate : function(){
21430         
21431         var cfg = {
21432             tag : 'div',
21433             cls : 'small-box ',
21434             cn : [
21435                 {
21436                     tag : 'div',
21437                     cls : 'inner',
21438                     cn :[
21439                         {
21440                             tag : 'h3',
21441                             cls : 'roo-headline',
21442                             html : this.headline
21443                         },
21444                         {
21445                             tag : 'p',
21446                             cls : 'roo-content',
21447                             html : this.content
21448                         }
21449                     ]
21450                 }
21451             ]
21452         }
21453         
21454         if(this.icon){
21455             cfg.cn.push({
21456                 tag : 'div',
21457                 cls : 'icon',
21458                 cn :[
21459                     {
21460                         tag : 'i',
21461                         cls : 'ion ' + this.icon
21462                     }
21463                 ]
21464             });
21465         }
21466         
21467         if(this.footer){
21468             var footer = {
21469                 tag : 'a',
21470                 cls : 'small-box-footer',
21471                 href : this.fhref || '#',
21472                 html : this.footer
21473             };
21474             
21475             cfg.cn.push(footer);
21476             
21477         }
21478         
21479         return  cfg;
21480     },
21481
21482     onRender : function(ct,position){
21483         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21484
21485
21486        
21487                 
21488     },
21489
21490     setHeadline: function (value)
21491     {
21492         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21493     },
21494     
21495     setFooter: function (value, href)
21496     {
21497         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21498         
21499         if(href){
21500             this.el.select('a.small-box-footer',true).first().attr('href', href);
21501         }
21502         
21503     },
21504
21505     setContent: function (value)
21506     {
21507         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21508     },
21509
21510     initEvents: function() 
21511     {   
21512         
21513     }
21514     
21515 });
21516
21517  
21518 /*
21519  * - LGPL
21520  *
21521  * TabBox
21522  * 
21523  */
21524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21525
21526 /**
21527  * @class Roo.bootstrap.dash.TabBox
21528  * @extends Roo.bootstrap.Component
21529  * Bootstrap TabBox class
21530  * @cfg {String} title Title of the TabBox
21531  * @cfg {String} icon Icon of the TabBox
21532  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21533  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21534  * 
21535  * @constructor
21536  * Create a new TabBox
21537  * @param {Object} config The config object
21538  */
21539
21540
21541 Roo.bootstrap.dash.TabBox = function(config){
21542     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21543     this.addEvents({
21544         // raw events
21545         /**
21546          * @event addpane
21547          * When a pane is added
21548          * @param {Roo.bootstrap.dash.TabPane} pane
21549          */
21550         "addpane" : true,
21551         /**
21552          * @event activatepane
21553          * When a pane is activated
21554          * @param {Roo.bootstrap.dash.TabPane} pane
21555          */
21556         "activatepane" : true
21557         
21558          
21559     });
21560     
21561     this.panes = [];
21562 };
21563
21564 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21565
21566     title : '',
21567     icon : false,
21568     showtabs : true,
21569     tabScrollable : false,
21570     
21571     getChildContainer : function()
21572     {
21573         return this.el.select('.tab-content', true).first();
21574     },
21575     
21576     getAutoCreate : function(){
21577         
21578         var header = {
21579             tag: 'li',
21580             cls: 'pull-left header',
21581             html: this.title,
21582             cn : []
21583         };
21584         
21585         if(this.icon){
21586             header.cn.push({
21587                 tag: 'i',
21588                 cls: 'fa ' + this.icon
21589             });
21590         }
21591         
21592         var h = {
21593             tag: 'ul',
21594             cls: 'nav nav-tabs pull-right',
21595             cn: [
21596                 header
21597             ]
21598         };
21599         
21600         if(this.tabScrollable){
21601             h = {
21602                 tag: 'div',
21603                 cls: 'tab-header',
21604                 cn: [
21605                     {
21606                         tag: 'ul',
21607                         cls: 'nav nav-tabs pull-right',
21608                         cn: [
21609                             header
21610                         ]
21611                     }
21612                 ]
21613             }
21614         }
21615         
21616         var cfg = {
21617             tag: 'div',
21618             cls: 'nav-tabs-custom',
21619             cn: [
21620                 h,
21621                 {
21622                     tag: 'div',
21623                     cls: 'tab-content no-padding',
21624                     cn: []
21625                 }
21626             ]
21627         }
21628
21629         return  cfg;
21630     },
21631     initEvents : function()
21632     {
21633         //Roo.log('add add pane handler');
21634         this.on('addpane', this.onAddPane, this);
21635     },
21636      /**
21637      * Updates the box title
21638      * @param {String} html to set the title to.
21639      */
21640     setTitle : function(value)
21641     {
21642         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21643     },
21644     onAddPane : function(pane)
21645     {
21646         this.panes.push(pane);
21647         //Roo.log('addpane');
21648         //Roo.log(pane);
21649         // tabs are rendere left to right..
21650         if(!this.showtabs){
21651             return;
21652         }
21653         
21654         var ctr = this.el.select('.nav-tabs', true).first();
21655          
21656          
21657         var existing = ctr.select('.nav-tab',true);
21658         var qty = existing.getCount();;
21659         
21660         
21661         var tab = ctr.createChild({
21662             tag : 'li',
21663             cls : 'nav-tab' + (qty ? '' : ' active'),
21664             cn : [
21665                 {
21666                     tag : 'a',
21667                     href:'#',
21668                     html : pane.title
21669                 }
21670             ]
21671         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21672         pane.tab = tab;
21673         
21674         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21675         if (!qty) {
21676             pane.el.addClass('active');
21677         }
21678         
21679                 
21680     },
21681     onTabClick : function(ev,un,ob,pane)
21682     {
21683         //Roo.log('tab - prev default');
21684         ev.preventDefault();
21685         
21686         
21687         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21688         pane.tab.addClass('active');
21689         //Roo.log(pane.title);
21690         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21691         // technically we should have a deactivate event.. but maybe add later.
21692         // and it should not de-activate the selected tab...
21693         this.fireEvent('activatepane', pane);
21694         pane.el.addClass('active');
21695         pane.fireEvent('activate');
21696         
21697         
21698     },
21699     
21700     getActivePane : function()
21701     {
21702         var r = false;
21703         Roo.each(this.panes, function(p) {
21704             if(p.el.hasClass('active')){
21705                 r = p;
21706                 return false;
21707             }
21708             
21709             return;
21710         });
21711         
21712         return r;
21713     }
21714     
21715     
21716 });
21717
21718  
21719 /*
21720  * - LGPL
21721  *
21722  * Tab pane
21723  * 
21724  */
21725 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21726 /**
21727  * @class Roo.bootstrap.TabPane
21728  * @extends Roo.bootstrap.Component
21729  * Bootstrap TabPane class
21730  * @cfg {Boolean} active (false | true) Default false
21731  * @cfg {String} title title of panel
21732
21733  * 
21734  * @constructor
21735  * Create a new TabPane
21736  * @param {Object} config The config object
21737  */
21738
21739 Roo.bootstrap.dash.TabPane = function(config){
21740     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21741     
21742     this.addEvents({
21743         // raw events
21744         /**
21745          * @event activate
21746          * When a pane is activated
21747          * @param {Roo.bootstrap.dash.TabPane} pane
21748          */
21749         "activate" : true
21750          
21751     });
21752 };
21753
21754 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21755     
21756     active : false,
21757     title : '',
21758     
21759     // the tabBox that this is attached to.
21760     tab : false,
21761      
21762     getAutoCreate : function() 
21763     {
21764         var cfg = {
21765             tag: 'div',
21766             cls: 'tab-pane'
21767         }
21768         
21769         if(this.active){
21770             cfg.cls += ' active';
21771         }
21772         
21773         return cfg;
21774     },
21775     initEvents  : function()
21776     {
21777         //Roo.log('trigger add pane handler');
21778         this.parent().fireEvent('addpane', this)
21779     },
21780     
21781      /**
21782      * Updates the tab title 
21783      * @param {String} html to set the title to.
21784      */
21785     setTitle: function(str)
21786     {
21787         if (!this.tab) {
21788             return;
21789         }
21790         this.title = str;
21791         this.tab.select('a', true).first().dom.innerHTML = str;
21792         
21793     }
21794     
21795     
21796     
21797 });
21798
21799  
21800
21801
21802  /*
21803  * - LGPL
21804  *
21805  * menu
21806  * 
21807  */
21808 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21809
21810 /**
21811  * @class Roo.bootstrap.menu.Menu
21812  * @extends Roo.bootstrap.Component
21813  * Bootstrap Menu class - container for Menu
21814  * @cfg {String} html Text of the menu
21815  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21816  * @cfg {String} icon Font awesome icon
21817  * @cfg {String} pos Menu align to (top | bottom) default bottom
21818  * 
21819  * 
21820  * @constructor
21821  * Create a new Menu
21822  * @param {Object} config The config object
21823  */
21824
21825
21826 Roo.bootstrap.menu.Menu = function(config){
21827     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21828     
21829     this.addEvents({
21830         /**
21831          * @event beforeshow
21832          * Fires before this menu is displayed
21833          * @param {Roo.bootstrap.menu.Menu} this
21834          */
21835         beforeshow : true,
21836         /**
21837          * @event beforehide
21838          * Fires before this menu is hidden
21839          * @param {Roo.bootstrap.menu.Menu} this
21840          */
21841         beforehide : true,
21842         /**
21843          * @event show
21844          * Fires after this menu is displayed
21845          * @param {Roo.bootstrap.menu.Menu} this
21846          */
21847         show : true,
21848         /**
21849          * @event hide
21850          * Fires after this menu is hidden
21851          * @param {Roo.bootstrap.menu.Menu} this
21852          */
21853         hide : true,
21854         /**
21855          * @event click
21856          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21857          * @param {Roo.bootstrap.menu.Menu} this
21858          * @param {Roo.EventObject} e
21859          */
21860         click : true
21861     });
21862     
21863 };
21864
21865 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21866     
21867     submenu : false,
21868     html : '',
21869     weight : 'default',
21870     icon : false,
21871     pos : 'bottom',
21872     
21873     
21874     getChildContainer : function() {
21875         if(this.isSubMenu){
21876             return this.el;
21877         }
21878         
21879         return this.el.select('ul.dropdown-menu', true).first();  
21880     },
21881     
21882     getAutoCreate : function()
21883     {
21884         var text = [
21885             {
21886                 tag : 'span',
21887                 cls : 'roo-menu-text',
21888                 html : this.html
21889             }
21890         ];
21891         
21892         if(this.icon){
21893             text.unshift({
21894                 tag : 'i',
21895                 cls : 'fa ' + this.icon
21896             })
21897         }
21898         
21899         
21900         var cfg = {
21901             tag : 'div',
21902             cls : 'btn-group',
21903             cn : [
21904                 {
21905                     tag : 'button',
21906                     cls : 'dropdown-button btn btn-' + this.weight,
21907                     cn : text
21908                 },
21909                 {
21910                     tag : 'button',
21911                     cls : 'dropdown-toggle btn btn-' + this.weight,
21912                     cn : [
21913                         {
21914                             tag : 'span',
21915                             cls : 'caret'
21916                         }
21917                     ]
21918                 },
21919                 {
21920                     tag : 'ul',
21921                     cls : 'dropdown-menu'
21922                 }
21923             ]
21924             
21925         };
21926         
21927         if(this.pos == 'top'){
21928             cfg.cls += ' dropup';
21929         }
21930         
21931         if(this.isSubMenu){
21932             cfg = {
21933                 tag : 'ul',
21934                 cls : 'dropdown-menu'
21935             }
21936         }
21937         
21938         return cfg;
21939     },
21940     
21941     onRender : function(ct, position)
21942     {
21943         this.isSubMenu = ct.hasClass('dropdown-submenu');
21944         
21945         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21946     },
21947     
21948     initEvents : function() 
21949     {
21950         if(this.isSubMenu){
21951             return;
21952         }
21953         
21954         this.hidden = true;
21955         
21956         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21957         this.triggerEl.on('click', this.onTriggerPress, this);
21958         
21959         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21960         this.buttonEl.on('click', this.onClick, this);
21961         
21962     },
21963     
21964     list : function()
21965     {
21966         if(this.isSubMenu){
21967             return this.el;
21968         }
21969         
21970         return this.el.select('ul.dropdown-menu', true).first();
21971     },
21972     
21973     onClick : function(e)
21974     {
21975         this.fireEvent("click", this, e);
21976     },
21977     
21978     onTriggerPress  : function(e)
21979     {   
21980         if (this.isVisible()) {
21981             this.hide();
21982         } else {
21983             this.show();
21984         }
21985     },
21986     
21987     isVisible : function(){
21988         return !this.hidden;
21989     },
21990     
21991     show : function()
21992     {
21993         this.fireEvent("beforeshow", this);
21994         
21995         this.hidden = false;
21996         this.el.addClass('open');
21997         
21998         Roo.get(document).on("mouseup", this.onMouseUp, this);
21999         
22000         this.fireEvent("show", this);
22001         
22002         
22003     },
22004     
22005     hide : function()
22006     {
22007         this.fireEvent("beforehide", this);
22008         
22009         this.hidden = true;
22010         this.el.removeClass('open');
22011         
22012         Roo.get(document).un("mouseup", this.onMouseUp);
22013         
22014         this.fireEvent("hide", this);
22015     },
22016     
22017     onMouseUp : function()
22018     {
22019         this.hide();
22020     }
22021     
22022 });
22023
22024  
22025  /*
22026  * - LGPL
22027  *
22028  * menu item
22029  * 
22030  */
22031 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22032
22033 /**
22034  * @class Roo.bootstrap.menu.Item
22035  * @extends Roo.bootstrap.Component
22036  * Bootstrap MenuItem class
22037  * @cfg {Boolean} submenu (true | false) default false
22038  * @cfg {String} html text of the item
22039  * @cfg {String} href the link
22040  * @cfg {Boolean} disable (true | false) default false
22041  * @cfg {Boolean} preventDefault (true | false) default true
22042  * @cfg {String} icon Font awesome icon
22043  * @cfg {String} pos Submenu align to (left | right) default right 
22044  * 
22045  * 
22046  * @constructor
22047  * Create a new Item
22048  * @param {Object} config The config object
22049  */
22050
22051
22052 Roo.bootstrap.menu.Item = function(config){
22053     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22054     this.addEvents({
22055         /**
22056          * @event mouseover
22057          * Fires when the mouse is hovering over this menu
22058          * @param {Roo.bootstrap.menu.Item} this
22059          * @param {Roo.EventObject} e
22060          */
22061         mouseover : true,
22062         /**
22063          * @event mouseout
22064          * Fires when the mouse exits this menu
22065          * @param {Roo.bootstrap.menu.Item} this
22066          * @param {Roo.EventObject} e
22067          */
22068         mouseout : true,
22069         // raw events
22070         /**
22071          * @event click
22072          * The raw click event for the entire grid.
22073          * @param {Roo.EventObject} e
22074          */
22075         click : true
22076     });
22077 };
22078
22079 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22080     
22081     submenu : false,
22082     href : '',
22083     html : '',
22084     preventDefault: true,
22085     disable : false,
22086     icon : false,
22087     pos : 'right',
22088     
22089     getAutoCreate : function()
22090     {
22091         var text = [
22092             {
22093                 tag : 'span',
22094                 cls : 'roo-menu-item-text',
22095                 html : this.html
22096             }
22097         ];
22098         
22099         if(this.icon){
22100             text.unshift({
22101                 tag : 'i',
22102                 cls : 'fa ' + this.icon
22103             })
22104         }
22105         
22106         var cfg = {
22107             tag : 'li',
22108             cn : [
22109                 {
22110                     tag : 'a',
22111                     href : this.href || '#',
22112                     cn : text
22113                 }
22114             ]
22115         };
22116         
22117         if(this.disable){
22118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22119         }
22120         
22121         if(this.submenu){
22122             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22123             
22124             if(this.pos == 'left'){
22125                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22126             }
22127         }
22128         
22129         return cfg;
22130     },
22131     
22132     initEvents : function() 
22133     {
22134         this.el.on('mouseover', this.onMouseOver, this);
22135         this.el.on('mouseout', this.onMouseOut, this);
22136         
22137         this.el.select('a', true).first().on('click', this.onClick, this);
22138         
22139     },
22140     
22141     onClick : function(e)
22142     {
22143         if(this.preventDefault){
22144             e.preventDefault();
22145         }
22146         
22147         this.fireEvent("click", this, e);
22148     },
22149     
22150     onMouseOver : function(e)
22151     {
22152         if(this.submenu && this.pos == 'left'){
22153             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22154         }
22155         
22156         this.fireEvent("mouseover", this, e);
22157     },
22158     
22159     onMouseOut : function(e)
22160     {
22161         this.fireEvent("mouseout", this, e);
22162     }
22163 });
22164
22165  
22166
22167  /*
22168  * - LGPL
22169  *
22170  * menu separator
22171  * 
22172  */
22173 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22174
22175 /**
22176  * @class Roo.bootstrap.menu.Separator
22177  * @extends Roo.bootstrap.Component
22178  * Bootstrap Separator class
22179  * 
22180  * @constructor
22181  * Create a new Separator
22182  * @param {Object} config The config object
22183  */
22184
22185
22186 Roo.bootstrap.menu.Separator = function(config){
22187     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22188 };
22189
22190 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22191     
22192     getAutoCreate : function(){
22193         var cfg = {
22194             tag : 'li',
22195             cls: 'divider'
22196         };
22197         
22198         return cfg;
22199     }
22200    
22201 });
22202
22203  
22204
22205  /*
22206  * - LGPL
22207  *
22208  * Tooltip
22209  * 
22210  */
22211
22212 /**
22213  * @class Roo.bootstrap.Tooltip
22214  * Bootstrap Tooltip class
22215  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22216  * to determine which dom element triggers the tooltip.
22217  * 
22218  * It needs to add support for additional attributes like tooltip-position
22219  * 
22220  * @constructor
22221  * Create a new Toolti
22222  * @param {Object} config The config object
22223  */
22224
22225 Roo.bootstrap.Tooltip = function(config){
22226     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22227 };
22228
22229 Roo.apply(Roo.bootstrap.Tooltip, {
22230     /**
22231      * @function init initialize tooltip monitoring.
22232      * @static
22233      */
22234     currentEl : false,
22235     currentTip : false,
22236     currentRegion : false,
22237     
22238     //  init : delay?
22239     
22240     init : function()
22241     {
22242         Roo.get(document).on('mouseover', this.enter ,this);
22243         Roo.get(document).on('mouseout', this.leave, this);
22244          
22245         
22246         this.currentTip = new Roo.bootstrap.Tooltip();
22247     },
22248     
22249     enter : function(ev)
22250     {
22251         var dom = ev.getTarget();
22252         
22253         //Roo.log(['enter',dom]);
22254         var el = Roo.fly(dom);
22255         if (this.currentEl) {
22256             //Roo.log(dom);
22257             //Roo.log(this.currentEl);
22258             //Roo.log(this.currentEl.contains(dom));
22259             if (this.currentEl == el) {
22260                 return;
22261             }
22262             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22263                 return;
22264             }
22265
22266         }
22267         
22268         
22269         
22270         if (this.currentTip.el) {
22271             this.currentTip.el.hide(); // force hiding...
22272         }    
22273         //Roo.log(ev);
22274         var bindEl = el;
22275         
22276         // you can not look for children, as if el is the body.. then everythign is the child..
22277         if (!el.attr('tooltip')) { //
22278             if (!el.select("[tooltip]").elements.length) {
22279                 return;
22280             }
22281             // is the mouse over this child...?
22282             bindEl = el.select("[tooltip]").first();
22283             var xy = ev.getXY();
22284             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22285                 //Roo.log("not in region.");
22286                 return;
22287             }
22288             //Roo.log("child element over..");
22289             
22290         }
22291         this.currentEl = bindEl;
22292         this.currentTip.bind(bindEl);
22293         this.currentRegion = Roo.lib.Region.getRegion(dom);
22294         this.currentTip.enter();
22295         
22296     },
22297     leave : function(ev)
22298     {
22299         var dom = ev.getTarget();
22300         //Roo.log(['leave',dom]);
22301         if (!this.currentEl) {
22302             return;
22303         }
22304         
22305         
22306         if (dom != this.currentEl.dom) {
22307             return;
22308         }
22309         var xy = ev.getXY();
22310         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22311             return;
22312         }
22313         // only activate leave if mouse cursor is outside... bounding box..
22314         
22315         
22316         
22317         
22318         if (this.currentTip) {
22319             this.currentTip.leave();
22320         }
22321         //Roo.log('clear currentEl');
22322         this.currentEl = false;
22323         
22324         
22325     },
22326     alignment : {
22327         'left' : ['r-l', [-2,0], 'right'],
22328         'right' : ['l-r', [2,0], 'left'],
22329         'bottom' : ['t-b', [0,2], 'top'],
22330         'top' : [ 'b-t', [0,-2], 'bottom']
22331     }
22332     
22333 });
22334
22335
22336 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22337     
22338     
22339     bindEl : false,
22340     
22341     delay : null, // can be { show : 300 , hide: 500}
22342     
22343     timeout : null,
22344     
22345     hoverState : null, //???
22346     
22347     placement : 'bottom', 
22348     
22349     getAutoCreate : function(){
22350     
22351         var cfg = {
22352            cls : 'tooltip',
22353            role : 'tooltip',
22354            cn : [
22355                 {
22356                     cls : 'tooltip-arrow'
22357                 },
22358                 {
22359                     cls : 'tooltip-inner'
22360                 }
22361            ]
22362         };
22363         
22364         return cfg;
22365     },
22366     bind : function(el)
22367     {
22368         this.bindEl = el;
22369     },
22370       
22371     
22372     enter : function () {
22373        
22374         if (this.timeout != null) {
22375             clearTimeout(this.timeout);
22376         }
22377         
22378         this.hoverState = 'in';
22379          //Roo.log("enter - show");
22380         if (!this.delay || !this.delay.show) {
22381             this.show();
22382             return;
22383         }
22384         var _t = this;
22385         this.timeout = setTimeout(function () {
22386             if (_t.hoverState == 'in') {
22387                 _t.show();
22388             }
22389         }, this.delay.show);
22390     },
22391     leave : function()
22392     {
22393         clearTimeout(this.timeout);
22394     
22395         this.hoverState = 'out';
22396          if (!this.delay || !this.delay.hide) {
22397             this.hide();
22398             return;
22399         }
22400        
22401         var _t = this;
22402         this.timeout = setTimeout(function () {
22403             //Roo.log("leave - timeout");
22404             
22405             if (_t.hoverState == 'out') {
22406                 _t.hide();
22407                 Roo.bootstrap.Tooltip.currentEl = false;
22408             }
22409         }, delay);
22410     },
22411     
22412     show : function ()
22413     {
22414         if (!this.el) {
22415             this.render(document.body);
22416         }
22417         // set content.
22418         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22419         
22420         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22421         
22422         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22423         
22424         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22425         
22426         var placement = typeof this.placement == 'function' ?
22427             this.placement.call(this, this.el, on_el) :
22428             this.placement;
22429             
22430         var autoToken = /\s?auto?\s?/i;
22431         var autoPlace = autoToken.test(placement);
22432         if (autoPlace) {
22433             placement = placement.replace(autoToken, '') || 'top';
22434         }
22435         
22436         //this.el.detach()
22437         //this.el.setXY([0,0]);
22438         this.el.show();
22439         //this.el.dom.style.display='block';
22440         this.el.addClass(placement);
22441         
22442         //this.el.appendTo(on_el);
22443         
22444         var p = this.getPosition();
22445         var box = this.el.getBox();
22446         
22447         if (autoPlace) {
22448             // fixme..
22449         }
22450         var align = Roo.bootstrap.Tooltip.alignment[placement];
22451         this.el.alignTo(this.bindEl, align[0],align[1]);
22452         //var arrow = this.el.select('.arrow',true).first();
22453         //arrow.set(align[2], 
22454         
22455         this.el.addClass('in fade');
22456         this.hoverState = null;
22457         
22458         if (this.el.hasClass('fade')) {
22459             // fade it?
22460         }
22461         
22462     },
22463     hide : function()
22464     {
22465          
22466         if (!this.el) {
22467             return;
22468         }
22469         //this.el.setXY([0,0]);
22470         this.el.removeClass('in');
22471         //this.el.hide();
22472         
22473     }
22474     
22475 });
22476  
22477
22478  /*
22479  * - LGPL
22480  *
22481  * Location Picker
22482  * 
22483  */
22484
22485 /**
22486  * @class Roo.bootstrap.LocationPicker
22487  * @extends Roo.bootstrap.Component
22488  * Bootstrap LocationPicker class
22489  * @cfg {Number} latitude Position when init default 0
22490  * @cfg {Number} longitude Position when init default 0
22491  * @cfg {Number} zoom default 15
22492  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22493  * @cfg {Boolean} mapTypeControl default false
22494  * @cfg {Boolean} disableDoubleClickZoom default false
22495  * @cfg {Boolean} scrollwheel default true
22496  * @cfg {Boolean} streetViewControl default false
22497  * @cfg {Number} radius default 0
22498  * @cfg {String} locationName
22499  * @cfg {Boolean} draggable default true
22500  * @cfg {Boolean} enableAutocomplete default false
22501  * @cfg {Boolean} enableReverseGeocode default true
22502  * @cfg {String} markerTitle
22503  * 
22504  * @constructor
22505  * Create a new LocationPicker
22506  * @param {Object} config The config object
22507  */
22508
22509
22510 Roo.bootstrap.LocationPicker = function(config){
22511     
22512     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22513     
22514     this.addEvents({
22515         /**
22516          * @event initial
22517          * Fires when the picker initialized.
22518          * @param {Roo.bootstrap.LocationPicker} this
22519          * @param {Google Location} location
22520          */
22521         initial : true,
22522         /**
22523          * @event positionchanged
22524          * Fires when the picker position changed.
22525          * @param {Roo.bootstrap.LocationPicker} this
22526          * @param {Google Location} location
22527          */
22528         positionchanged : true,
22529         /**
22530          * @event resize
22531          * Fires when the map resize.
22532          * @param {Roo.bootstrap.LocationPicker} this
22533          */
22534         resize : true,
22535         /**
22536          * @event show
22537          * Fires when the map show.
22538          * @param {Roo.bootstrap.LocationPicker} this
22539          */
22540         show : true,
22541         /**
22542          * @event hide
22543          * Fires when the map hide.
22544          * @param {Roo.bootstrap.LocationPicker} this
22545          */
22546         hide : true,
22547         /**
22548          * @event mapClick
22549          * Fires when click the map.
22550          * @param {Roo.bootstrap.LocationPicker} this
22551          * @param {Map event} e
22552          */
22553         mapClick : true,
22554         /**
22555          * @event mapRightClick
22556          * Fires when right click the map.
22557          * @param {Roo.bootstrap.LocationPicker} this
22558          * @param {Map event} e
22559          */
22560         mapRightClick : true,
22561         /**
22562          * @event markerClick
22563          * Fires when click the marker.
22564          * @param {Roo.bootstrap.LocationPicker} this
22565          * @param {Map event} e
22566          */
22567         markerClick : true,
22568         /**
22569          * @event markerRightClick
22570          * Fires when right click the marker.
22571          * @param {Roo.bootstrap.LocationPicker} this
22572          * @param {Map event} e
22573          */
22574         markerRightClick : true,
22575         /**
22576          * @event OverlayViewDraw
22577          * Fires when OverlayView Draw
22578          * @param {Roo.bootstrap.LocationPicker} this
22579          */
22580         OverlayViewDraw : true,
22581         /**
22582          * @event OverlayViewOnAdd
22583          * Fires when OverlayView Draw
22584          * @param {Roo.bootstrap.LocationPicker} this
22585          */
22586         OverlayViewOnAdd : true,
22587         /**
22588          * @event OverlayViewOnRemove
22589          * Fires when OverlayView Draw
22590          * @param {Roo.bootstrap.LocationPicker} this
22591          */
22592         OverlayViewOnRemove : true,
22593         /**
22594          * @event OverlayViewShow
22595          * Fires when OverlayView Draw
22596          * @param {Roo.bootstrap.LocationPicker} this
22597          * @param {Pixel} cpx
22598          */
22599         OverlayViewShow : true,
22600         /**
22601          * @event OverlayViewHide
22602          * Fires when OverlayView Draw
22603          * @param {Roo.bootstrap.LocationPicker} this
22604          */
22605         OverlayViewHide : true
22606     });
22607         
22608 };
22609
22610 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22611     
22612     gMapContext: false,
22613     
22614     latitude: 0,
22615     longitude: 0,
22616     zoom: 15,
22617     mapTypeId: false,
22618     mapTypeControl: false,
22619     disableDoubleClickZoom: false,
22620     scrollwheel: true,
22621     streetViewControl: false,
22622     radius: 0,
22623     locationName: '',
22624     draggable: true,
22625     enableAutocomplete: false,
22626     enableReverseGeocode: true,
22627     markerTitle: '',
22628     
22629     getAutoCreate: function()
22630     {
22631
22632         var cfg = {
22633             tag: 'div',
22634             cls: 'roo-location-picker'
22635         };
22636         
22637         return cfg
22638     },
22639     
22640     initEvents: function(ct, position)
22641     {       
22642         if(!this.el.getWidth() || this.isApplied()){
22643             return;
22644         }
22645         
22646         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22647         
22648         this.initial();
22649     },
22650     
22651     initial: function()
22652     {
22653         if(!this.mapTypeId){
22654             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22655         }
22656         
22657         this.gMapContext = this.GMapContext();
22658         
22659         this.initOverlayView();
22660         
22661         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22662         
22663         var _this = this;
22664                 
22665         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22666             _this.setPosition(_this.gMapContext.marker.position);
22667         });
22668         
22669         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22670             _this.fireEvent('mapClick', this, event);
22671             
22672         });
22673
22674         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22675             _this.fireEvent('mapRightClick', this, event);
22676             
22677         });
22678         
22679         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22680             _this.fireEvent('markerClick', this, event);
22681             
22682         });
22683
22684         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22685             _this.fireEvent('markerRightClick', this, event);
22686             
22687         });
22688         
22689         this.setPosition(this.gMapContext.location);
22690         
22691         this.fireEvent('initial', this, this.gMapContext.location);
22692     },
22693     
22694     initOverlayView: function()
22695     {
22696         var _this = this;
22697         
22698         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22699             
22700             draw: function()
22701             {
22702                 _this.fireEvent('OverlayViewDraw', _this);
22703             },
22704             
22705             onAdd: function()
22706             {
22707                 _this.fireEvent('OverlayViewOnAdd', _this);
22708             },
22709             
22710             onRemove: function()
22711             {
22712                 _this.fireEvent('OverlayViewOnRemove', _this);
22713             },
22714             
22715             show: function(cpx)
22716             {
22717                 _this.fireEvent('OverlayViewShow', _this, cpx);
22718             },
22719             
22720             hide: function()
22721             {
22722                 _this.fireEvent('OverlayViewHide', _this);
22723             }
22724             
22725         });
22726     },
22727     
22728     fromLatLngToContainerPixel: function(event)
22729     {
22730         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22731     },
22732     
22733     isApplied: function() 
22734     {
22735         return this.getGmapContext() == false ? false : true;
22736     },
22737     
22738     getGmapContext: function() 
22739     {
22740         return this.gMapContext
22741     },
22742     
22743     GMapContext: function() 
22744     {
22745         var position = new google.maps.LatLng(this.latitude, this.longitude);
22746         
22747         var _map = new google.maps.Map(this.el.dom, {
22748             center: position,
22749             zoom: this.zoom,
22750             mapTypeId: this.mapTypeId,
22751             mapTypeControl: this.mapTypeControl,
22752             disableDoubleClickZoom: this.disableDoubleClickZoom,
22753             scrollwheel: this.scrollwheel,
22754             streetViewControl: this.streetViewControl,
22755             locationName: this.locationName,
22756             draggable: this.draggable,
22757             enableAutocomplete: this.enableAutocomplete,
22758             enableReverseGeocode: this.enableReverseGeocode
22759         });
22760         
22761         var _marker = new google.maps.Marker({
22762             position: position,
22763             map: _map,
22764             title: this.markerTitle,
22765             draggable: this.draggable
22766         });
22767         
22768         return {
22769             map: _map,
22770             marker: _marker,
22771             circle: null,
22772             location: position,
22773             radius: this.radius,
22774             locationName: this.locationName,
22775             addressComponents: {
22776                 formatted_address: null,
22777                 addressLine1: null,
22778                 addressLine2: null,
22779                 streetName: null,
22780                 streetNumber: null,
22781                 city: null,
22782                 district: null,
22783                 state: null,
22784                 stateOrProvince: null
22785             },
22786             settings: this,
22787             domContainer: this.el.dom,
22788             geodecoder: new google.maps.Geocoder()
22789         };
22790     },
22791     
22792     drawCircle: function(center, radius, options) 
22793     {
22794         if (this.gMapContext.circle != null) {
22795             this.gMapContext.circle.setMap(null);
22796         }
22797         if (radius > 0) {
22798             radius *= 1;
22799             options = Roo.apply({}, options, {
22800                 strokeColor: "#0000FF",
22801                 strokeOpacity: .35,
22802                 strokeWeight: 2,
22803                 fillColor: "#0000FF",
22804                 fillOpacity: .2
22805             });
22806             
22807             options.map = this.gMapContext.map;
22808             options.radius = radius;
22809             options.center = center;
22810             this.gMapContext.circle = new google.maps.Circle(options);
22811             return this.gMapContext.circle;
22812         }
22813         
22814         return null;
22815     },
22816     
22817     setPosition: function(location) 
22818     {
22819         this.gMapContext.location = location;
22820         this.gMapContext.marker.setPosition(location);
22821         this.gMapContext.map.panTo(location);
22822         this.drawCircle(location, this.gMapContext.radius, {});
22823         
22824         var _this = this;
22825         
22826         if (this.gMapContext.settings.enableReverseGeocode) {
22827             this.gMapContext.geodecoder.geocode({
22828                 latLng: this.gMapContext.location
22829             }, function(results, status) {
22830                 
22831                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22832                     _this.gMapContext.locationName = results[0].formatted_address;
22833                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22834                     
22835                     _this.fireEvent('positionchanged', this, location);
22836                 }
22837             });
22838             
22839             return;
22840         }
22841         
22842         this.fireEvent('positionchanged', this, location);
22843     },
22844     
22845     resize: function()
22846     {
22847         google.maps.event.trigger(this.gMapContext.map, "resize");
22848         
22849         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22850         
22851         this.fireEvent('resize', this);
22852     },
22853     
22854     setPositionByLatLng: function(latitude, longitude)
22855     {
22856         this.setPosition(new google.maps.LatLng(latitude, longitude));
22857     },
22858     
22859     getCurrentPosition: function() 
22860     {
22861         return {
22862             latitude: this.gMapContext.location.lat(),
22863             longitude: this.gMapContext.location.lng()
22864         };
22865     },
22866     
22867     getAddressName: function() 
22868     {
22869         return this.gMapContext.locationName;
22870     },
22871     
22872     getAddressComponents: function() 
22873     {
22874         return this.gMapContext.addressComponents;
22875     },
22876     
22877     address_component_from_google_geocode: function(address_components) 
22878     {
22879         var result = {};
22880         
22881         for (var i = 0; i < address_components.length; i++) {
22882             var component = address_components[i];
22883             if (component.types.indexOf("postal_code") >= 0) {
22884                 result.postalCode = component.short_name;
22885             } else if (component.types.indexOf("street_number") >= 0) {
22886                 result.streetNumber = component.short_name;
22887             } else if (component.types.indexOf("route") >= 0) {
22888                 result.streetName = component.short_name;
22889             } else if (component.types.indexOf("neighborhood") >= 0) {
22890                 result.city = component.short_name;
22891             } else if (component.types.indexOf("locality") >= 0) {
22892                 result.city = component.short_name;
22893             } else if (component.types.indexOf("sublocality") >= 0) {
22894                 result.district = component.short_name;
22895             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22896                 result.stateOrProvince = component.short_name;
22897             } else if (component.types.indexOf("country") >= 0) {
22898                 result.country = component.short_name;
22899             }
22900         }
22901         
22902         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22903         result.addressLine2 = "";
22904         return result;
22905     },
22906     
22907     setZoomLevel: function(zoom)
22908     {
22909         this.gMapContext.map.setZoom(zoom);
22910     },
22911     
22912     show: function()
22913     {
22914         if(!this.el){
22915             return;
22916         }
22917         
22918         this.el.show();
22919         
22920         this.resize();
22921         
22922         this.fireEvent('show', this);
22923     },
22924     
22925     hide: function()
22926     {
22927         if(!this.el){
22928             return;
22929         }
22930         
22931         this.el.hide();
22932         
22933         this.fireEvent('hide', this);
22934     }
22935     
22936 });
22937
22938 Roo.apply(Roo.bootstrap.LocationPicker, {
22939     
22940     OverlayView : function(map, options)
22941     {
22942         options = options || {};
22943         
22944         this.setMap(map);
22945     }
22946     
22947     
22948 });/*
22949  * - LGPL
22950  *
22951  * Alert
22952  * 
22953  */
22954
22955 /**
22956  * @class Roo.bootstrap.Alert
22957  * @extends Roo.bootstrap.Component
22958  * Bootstrap Alert class
22959  * @cfg {String} title The title of alert
22960  * @cfg {String} html The content of alert
22961  * @cfg {String} weight (  success | info | warning | danger )
22962  * @cfg {String} faicon font-awesomeicon
22963  * 
22964  * @constructor
22965  * Create a new alert
22966  * @param {Object} config The config object
22967  */
22968
22969
22970 Roo.bootstrap.Alert = function(config){
22971     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22972     
22973 };
22974
22975 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22976     
22977     title: '',
22978     html: '',
22979     weight: false,
22980     faicon: false,
22981     
22982     getAutoCreate : function()
22983     {
22984         
22985         var cfg = {
22986             tag : 'div',
22987             cls : 'alert',
22988             cn : [
22989                 {
22990                     tag : 'i',
22991                     cls : 'roo-alert-icon'
22992                     
22993                 },
22994                 {
22995                     tag : 'b',
22996                     cls : 'roo-alert-title',
22997                     html : this.title
22998                 },
22999                 {
23000                     tag : 'span',
23001                     cls : 'roo-alert-text',
23002                     html : this.html
23003                 }
23004             ]
23005         };
23006         
23007         if(this.faicon){
23008             cfg.cn[0].cls += ' fa ' + this.faicon;
23009         }
23010         
23011         if(this.weight){
23012             cfg.cls += ' alert-' + this.weight;
23013         }
23014         
23015         return cfg;
23016     },
23017     
23018     initEvents: function() 
23019     {
23020         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23021     },
23022     
23023     setTitle : function(str)
23024     {
23025         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23026     },
23027     
23028     setText : function(str)
23029     {
23030         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23031     },
23032     
23033     setWeight : function(weight)
23034     {
23035         if(this.weight){
23036             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23037         }
23038         
23039         this.weight = weight;
23040         
23041         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23042     },
23043     
23044     setIcon : function(icon)
23045     {
23046         if(this.faicon){
23047             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23048         }
23049         
23050         this.faicon = icon
23051         
23052         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23053     },
23054     
23055     hide: function() 
23056     {
23057         this.el.hide();   
23058     },
23059     
23060     show: function() 
23061     {  
23062         this.el.show();   
23063     }
23064     
23065 });
23066
23067