Roo/bootstrap/Container.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     expanded : true,
1035   
1036      
1037     getChildContainer : function() {
1038         
1039         if(!this.el){
1040             return false;
1041         }
1042         
1043         if (this.panel.length) {
1044             return this.el.select('.panel-body',true).first();
1045         }
1046         
1047         return this.el;
1048     },
1049     
1050     
1051     getAutoCreate : function(){
1052         
1053         var cfg = {
1054             tag : this.tag || 'div',
1055             html : '',
1056             cls : ''
1057         };
1058         if (this.jumbotron) {
1059             cfg.cls = 'jumbotron';
1060         }
1061         
1062         
1063         
1064         // - this is applied by the parent..
1065         //if (this.cls) {
1066         //    cfg.cls = this.cls + '';
1067         //}
1068         
1069         if (this.sticky.length) {
1070             
1071             var bd = Roo.get(document.body);
1072             if (!bd.hasClass('bootstrap-sticky')) {
1073                 bd.addClass('bootstrap-sticky');
1074                 Roo.select('html',true).setStyle('height', '100%');
1075             }
1076              
1077             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1078         }
1079         
1080         
1081         if (this.well.length) {
1082             switch (this.well) {
1083                 case 'lg':
1084                 case 'sm':
1085                     cfg.cls +=' well well-' +this.well;
1086                     break;
1087                 default:
1088                     cfg.cls +=' well';
1089                     break;
1090             }
1091         }
1092         
1093         if (this.hidden) {
1094             cfg.cls += ' hidden';
1095         }
1096         
1097         
1098         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1099             cfg.cls +=' alert alert-' + this.alert;
1100         }
1101         
1102         var body = cfg;
1103         
1104         if (this.panel.length) {
1105             cfg.cls += ' panel panel-' + this.panel;
1106             cfg.cn = [];
1107             if (this.header.length) {
1108                 
1109                 var h = [];
1110                 
1111                 if(this.expandable){
1112                     h.push({
1113                         tag: 'i',
1114                         cls: 'fa fa-minus'
1115                     });
1116                 }
1117                 
1118                 h.push(
1119                     {
1120                         tag: 'span',
1121                         cls : 'panel-title',
1122                         html : this.header
1123                     },
1124                     {
1125                         tag: 'span',
1126                         cls: 'panel-header-right',
1127                         html: this.rheader
1128                     }
1129                 );
1130                 
1131                 cfg.cn.push({
1132                     cls : 'panel-heading',
1133                     cn : h
1134                 });
1135                 
1136             }
1137             
1138             body = false;
1139             cfg.cn.push({
1140                 cls : 'panel-body',
1141                 html : this.html
1142             });
1143             
1144             
1145             if (this.footer.length) {
1146                 cfg.cn.push({
1147                     cls : 'panel-footer',
1148                     html : this.footer
1149                     
1150                 });
1151             }
1152             
1153         }
1154         
1155         if (body) {
1156             body.html = this.html || cfg.html;
1157             // prefix with the icons..
1158             if (this.fa) {
1159                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1160             }
1161             if (this.icon) {
1162                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1163             }
1164             
1165             
1166         }
1167         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1168             cfg.cls =  'container';
1169         }
1170         
1171         return cfg;
1172     },
1173     
1174     initEvents: function() 
1175     {
1176         if(!this.expandable){
1177             return;
1178         }
1179         
1180         var headerEl = this.headerEl();
1181         
1182         if(!headerEl){
1183             return;
1184         }
1185         
1186         headerEl.on('click', this.onToggleClick, this);
1187         
1188     },
1189     
1190     onToggleClick : function()
1191     {
1192         var headerEl = this.headerEl();
1193         
1194         if(!headerEl){
1195             return;
1196         }
1197         
1198         if(this.expanded){
1199             this.collapse();
1200             return;
1201         }
1202         
1203         this.expand();
1204     },
1205     
1206     expand : function()
1207     {
1208         if(this.fireEvent('expand', this)) {
1209             
1210             this.expanded = true;
1211             
1212             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1213         
1214             var toggleEl = this.toggleEl();
1215
1216             if(!toggleEl){
1217                 return;
1218             }
1219
1220             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1221         }
1222         
1223     },
1224     
1225     collapse : function()
1226     {
1227         if(this.fireEvent('collapse', this)) {
1228             
1229             this.expanded = false;
1230             
1231             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1232         
1233             var toggleEl = this.toggleEl();
1234
1235             if(!toggleEl){
1236                 return;
1237             }
1238
1239             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1240         }
1241     },
1242     
1243     toggleEl : function()
1244     {
1245         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1246             return;
1247         }
1248         
1249         return this.el.select('.panel-heading .fa',true).first();
1250     },
1251     
1252     headerEl : function()
1253     {
1254         if(!this.el || !this.panel.length || !this.header.length){
1255             return;
1256         }
1257         
1258         return this.el.select('.panel-heading',true).first()
1259     },
1260     
1261     titleEl : function()
1262     {
1263         if(!this.el || !this.panel.length || !this.header.length){
1264             return;
1265         }
1266         
1267         return this.el.select('.panel-title',true).first();
1268     },
1269     
1270     setTitle : function(v)
1271     {
1272         var titleEl = this.titleEl();
1273         
1274         if(!titleEl){
1275             return;
1276         }
1277         
1278         titleEl.dom.innerHTML = v;
1279     },
1280     
1281     getTitle : function()
1282     {
1283         
1284         var titleEl = this.titleEl();
1285         
1286         if(!titleEl){
1287             return '';
1288         }
1289         
1290         return titleEl.dom.innerHTML;
1291     },
1292     
1293     setRightTitle : function(v)
1294     {
1295         var t = this.el.select('.panel-header-right',true).first();
1296         
1297         if(!t){
1298             return;
1299         }
1300         
1301         t.dom.innerHTML = v;
1302     }
1303    
1304 });
1305
1306  /*
1307  * - LGPL
1308  *
1309  * image
1310  * 
1311  */
1312
1313
1314 /**
1315  * @class Roo.bootstrap.Img
1316  * @extends Roo.bootstrap.Component
1317  * Bootstrap Img class
1318  * @cfg {Boolean} imgResponsive false | true
1319  * @cfg {String} border rounded | circle | thumbnail
1320  * @cfg {String} src image source
1321  * @cfg {String} alt image alternative text
1322  * @cfg {String} href a tag href
1323  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1324  * 
1325  * @constructor
1326  * Create a new Input
1327  * @param {Object} config The config object
1328  */
1329
1330 Roo.bootstrap.Img = function(config){
1331     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1332     
1333     this.addEvents({
1334         // img events
1335         /**
1336          * @event click
1337          * The img click event for the img.
1338          * @param {Roo.EventObject} e
1339          */
1340         "click" : true
1341     });
1342 };
1343
1344 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1345     
1346     imgResponsive: true,
1347     border: '',
1348     src: '',
1349     href: false,
1350     target: false,
1351
1352     getAutoCreate : function(){
1353         
1354         var cfg = {
1355             tag: 'img',
1356             cls: (this.imgResponsive) ? 'img-responsive' : '',
1357             html : null
1358         }
1359         
1360         cfg.html = this.html || cfg.html;
1361         
1362         cfg.src = this.src || cfg.src;
1363         
1364         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1365             cfg.cls += ' img-' + this.border;
1366         }
1367         
1368         if(this.alt){
1369             cfg.alt = this.alt;
1370         }
1371         
1372         if(this.href){
1373             var a = {
1374                 tag: 'a',
1375                 href: this.href,
1376                 cn: [
1377                     cfg
1378                 ]
1379             }
1380             
1381             if(this.target){
1382                 a.target = this.target;
1383             }
1384             
1385         }
1386         
1387         
1388         return (this.href) ? a : cfg;
1389     },
1390     
1391     initEvents: function() {
1392         
1393         if(!this.href){
1394             this.el.on('click', this.onClick, this);
1395         }
1396     },
1397     
1398     onClick : function(e)
1399     {
1400         Roo.log('img onclick');
1401         this.fireEvent('click', this, e);
1402     }
1403    
1404 });
1405
1406  /*
1407  * - LGPL
1408  *
1409  * image
1410  * 
1411  */
1412
1413
1414 /**
1415  * @class Roo.bootstrap.Link
1416  * @extends Roo.bootstrap.Component
1417  * Bootstrap Link Class
1418  * @cfg {String} alt image alternative text
1419  * @cfg {String} href a tag href
1420  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1421  * @cfg {String} html the content of the link.
1422  * @cfg {String} anchor name for the anchor link
1423
1424  * @cfg {Boolean} preventDefault (true | false) default false
1425
1426  * 
1427  * @constructor
1428  * Create a new Input
1429  * @param {Object} config The config object
1430  */
1431
1432 Roo.bootstrap.Link = function(config){
1433     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1434     
1435     this.addEvents({
1436         // img events
1437         /**
1438          * @event click
1439          * The img click event for the img.
1440          * @param {Roo.EventObject} e
1441          */
1442         "click" : true
1443     });
1444 };
1445
1446 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1447     
1448     href: false,
1449     target: false,
1450     preventDefault: false,
1451     anchor : false,
1452     alt : false,
1453
1454     getAutoCreate : function()
1455     {
1456         
1457         var cfg = {
1458             tag: 'a'
1459         };
1460         // anchor's do not require html/href...
1461         if (this.anchor === false) {
1462             cfg.html = this.html || 'html-missing';
1463             cfg.href = this.href || '#';
1464         } else {
1465             cfg.name = this.anchor;
1466             if (this.html !== false) {
1467                 cfg.html = this.html;
1468             }
1469             if (this.href !== false) {
1470                 cfg.href = this.href;
1471             }
1472         }
1473         
1474         if(this.alt !== false){
1475             cfg.alt = this.alt;
1476         }
1477         
1478         
1479         if(this.target !== false) {
1480             cfg.target = this.target;
1481         }
1482         
1483         return cfg;
1484     },
1485     
1486     initEvents: function() {
1487         
1488         if(!this.href || this.preventDefault){
1489             this.el.on('click', this.onClick, this);
1490         }
1491     },
1492     
1493     onClick : function(e)
1494     {
1495         if(this.preventDefault){
1496             e.preventDefault();
1497         }
1498         //Roo.log('img onclick');
1499         this.fireEvent('click', this, e);
1500     }
1501    
1502 });
1503
1504  /*
1505  * - LGPL
1506  *
1507  * header
1508  * 
1509  */
1510
1511 /**
1512  * @class Roo.bootstrap.Header
1513  * @extends Roo.bootstrap.Component
1514  * Bootstrap Header class
1515  * @cfg {String} html content of header
1516  * @cfg {Number} level (1|2|3|4|5|6) default 1
1517  * 
1518  * @constructor
1519  * Create a new Header
1520  * @param {Object} config The config object
1521  */
1522
1523
1524 Roo.bootstrap.Header  = function(config){
1525     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1526 };
1527
1528 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1529     
1530     //href : false,
1531     html : false,
1532     level : 1,
1533     
1534     
1535     
1536     getAutoCreate : function(){
1537         
1538         
1539         
1540         var cfg = {
1541             tag: 'h' + (1 *this.level),
1542             html: this.html || ''
1543         } ;
1544         
1545         return cfg;
1546     }
1547    
1548 });
1549
1550  
1551
1552  /*
1553  * Based on:
1554  * Ext JS Library 1.1.1
1555  * Copyright(c) 2006-2007, Ext JS, LLC.
1556  *
1557  * Originally Released Under LGPL - original licence link has changed is not relivant.
1558  *
1559  * Fork - LGPL
1560  * <script type="text/javascript">
1561  */
1562  
1563 /**
1564  * @class Roo.bootstrap.MenuMgr
1565  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1566  * @singleton
1567  */
1568 Roo.bootstrap.MenuMgr = function(){
1569    var menus, active, groups = {}, attached = false, lastShow = new Date();
1570
1571    // private - called when first menu is created
1572    function init(){
1573        menus = {};
1574        active = new Roo.util.MixedCollection();
1575        Roo.get(document).addKeyListener(27, function(){
1576            if(active.length > 0){
1577                hideAll();
1578            }
1579        });
1580    }
1581
1582    // private
1583    function hideAll(){
1584        if(active && active.length > 0){
1585            var c = active.clone();
1586            c.each(function(m){
1587                m.hide();
1588            });
1589        }
1590    }
1591
1592    // private
1593    function onHide(m){
1594        active.remove(m);
1595        if(active.length < 1){
1596            Roo.get(document).un("mouseup", onMouseDown);
1597             
1598            attached = false;
1599        }
1600    }
1601
1602    // private
1603    function onShow(m){
1604        var last = active.last();
1605        lastShow = new Date();
1606        active.add(m);
1607        if(!attached){
1608           Roo.get(document).on("mouseup", onMouseDown);
1609            
1610            attached = true;
1611        }
1612        if(m.parentMenu){
1613           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1614           m.parentMenu.activeChild = m;
1615        }else if(last && last.isVisible()){
1616           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1617        }
1618    }
1619
1620    // private
1621    function onBeforeHide(m){
1622        if(m.activeChild){
1623            m.activeChild.hide();
1624        }
1625        if(m.autoHideTimer){
1626            clearTimeout(m.autoHideTimer);
1627            delete m.autoHideTimer;
1628        }
1629    }
1630
1631    // private
1632    function onBeforeShow(m){
1633        var pm = m.parentMenu;
1634        if(!pm && !m.allowOtherMenus){
1635            hideAll();
1636        }else if(pm && pm.activeChild && active != m){
1637            pm.activeChild.hide();
1638        }
1639    }
1640
1641    // private
1642    function onMouseDown(e){
1643         Roo.log("on MouseDown");
1644         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1645             Roo.log("hideAll");
1646             hideAll();
1647             e.stopEvent();
1648         }
1649         
1650         
1651    }
1652
1653    // private
1654    function onBeforeCheck(mi, state){
1655        if(state){
1656            var g = groups[mi.group];
1657            for(var i = 0, l = g.length; i < l; i++){
1658                if(g[i] != mi){
1659                    g[i].setChecked(false);
1660                }
1661            }
1662        }
1663    }
1664
1665    return {
1666
1667        /**
1668         * Hides all menus that are currently visible
1669         */
1670        hideAll : function(){
1671             hideAll();  
1672        },
1673
1674        // private
1675        register : function(menu){
1676            if(!menus){
1677                init();
1678            }
1679            menus[menu.id] = menu;
1680            menu.on("beforehide", onBeforeHide);
1681            menu.on("hide", onHide);
1682            menu.on("beforeshow", onBeforeShow);
1683            menu.on("show", onShow);
1684            var g = menu.group;
1685            if(g && menu.events["checkchange"]){
1686                if(!groups[g]){
1687                    groups[g] = [];
1688                }
1689                groups[g].push(menu);
1690                menu.on("checkchange", onCheck);
1691            }
1692        },
1693
1694         /**
1695          * Returns a {@link Roo.menu.Menu} object
1696          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1697          * be used to generate and return a new Menu instance.
1698          */
1699        get : function(menu){
1700            if(typeof menu == "string"){ // menu id
1701                return menus[menu];
1702            }else if(menu.events){  // menu instance
1703                return menu;
1704            }
1705            /*else if(typeof menu.length == 'number'){ // array of menu items?
1706                return new Roo.bootstrap.Menu({items:menu});
1707            }else{ // otherwise, must be a config
1708                return new Roo.bootstrap.Menu(menu);
1709            }
1710            */
1711            return false;
1712        },
1713
1714        // private
1715        unregister : function(menu){
1716            delete menus[menu.id];
1717            menu.un("beforehide", onBeforeHide);
1718            menu.un("hide", onHide);
1719            menu.un("beforeshow", onBeforeShow);
1720            menu.un("show", onShow);
1721            var g = menu.group;
1722            if(g && menu.events["checkchange"]){
1723                groups[g].remove(menu);
1724                menu.un("checkchange", onCheck);
1725            }
1726        },
1727
1728        // private
1729        registerCheckable : function(menuItem){
1730            var g = menuItem.group;
1731            if(g){
1732                if(!groups[g]){
1733                    groups[g] = [];
1734                }
1735                groups[g].push(menuItem);
1736                menuItem.on("beforecheckchange", onBeforeCheck);
1737            }
1738        },
1739
1740        // private
1741        unregisterCheckable : function(menuItem){
1742            var g = menuItem.group;
1743            if(g){
1744                groups[g].remove(menuItem);
1745                menuItem.un("beforecheckchange", onBeforeCheck);
1746            }
1747        }
1748    };
1749 }();/*
1750  * - LGPL
1751  *
1752  * menu
1753  * 
1754  */
1755
1756 /**
1757  * @class Roo.bootstrap.Menu
1758  * @extends Roo.bootstrap.Component
1759  * Bootstrap Menu class - container for MenuItems
1760  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1761  * 
1762  * @constructor
1763  * Create a new Menu
1764  * @param {Object} config The config object
1765  */
1766
1767
1768 Roo.bootstrap.Menu = function(config){
1769     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1770     if (this.registerMenu) {
1771         Roo.bootstrap.MenuMgr.register(this);
1772     }
1773     this.addEvents({
1774         /**
1775          * @event beforeshow
1776          * Fires before this menu is displayed
1777          * @param {Roo.menu.Menu} this
1778          */
1779         beforeshow : true,
1780         /**
1781          * @event beforehide
1782          * Fires before this menu is hidden
1783          * @param {Roo.menu.Menu} this
1784          */
1785         beforehide : true,
1786         /**
1787          * @event show
1788          * Fires after this menu is displayed
1789          * @param {Roo.menu.Menu} this
1790          */
1791         show : true,
1792         /**
1793          * @event hide
1794          * Fires after this menu is hidden
1795          * @param {Roo.menu.Menu} this
1796          */
1797         hide : true,
1798         /**
1799          * @event click
1800          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1801          * @param {Roo.menu.Menu} this
1802          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1803          * @param {Roo.EventObject} e
1804          */
1805         click : true,
1806         /**
1807          * @event mouseover
1808          * Fires when the mouse is hovering over this menu
1809          * @param {Roo.menu.Menu} this
1810          * @param {Roo.EventObject} e
1811          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1812          */
1813         mouseover : true,
1814         /**
1815          * @event mouseout
1816          * Fires when the mouse exits this menu
1817          * @param {Roo.menu.Menu} this
1818          * @param {Roo.EventObject} e
1819          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1820          */
1821         mouseout : true,
1822         /**
1823          * @event itemclick
1824          * Fires when a menu item contained in this menu is clicked
1825          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1826          * @param {Roo.EventObject} e
1827          */
1828         itemclick: true
1829     });
1830     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1831 };
1832
1833 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1834     
1835    /// html : false,
1836     //align : '',
1837     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1838     type: false,
1839     /**
1840      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1841      */
1842     registerMenu : true,
1843     
1844     menuItems :false, // stores the menu items..
1845     
1846     hidden:true,
1847     
1848     parentMenu : false,
1849     
1850     getChildContainer : function() {
1851         return this.el;  
1852     },
1853     
1854     getAutoCreate : function(){
1855          
1856         //if (['right'].indexOf(this.align)!==-1) {
1857         //    cfg.cn[1].cls += ' pull-right'
1858         //}
1859         
1860         
1861         var cfg = {
1862             tag : 'ul',
1863             cls : 'dropdown-menu' ,
1864             style : 'z-index:1000'
1865             
1866         }
1867         
1868         if (this.type === 'submenu') {
1869             cfg.cls = 'submenu active';
1870         }
1871         if (this.type === 'treeview') {
1872             cfg.cls = 'treeview-menu';
1873         }
1874         
1875         return cfg;
1876     },
1877     initEvents : function() {
1878         
1879        // Roo.log("ADD event");
1880        // Roo.log(this.triggerEl.dom);
1881         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1882         
1883         this.triggerEl.addClass('dropdown-toggle');
1884         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1885
1886         this.el.on("mouseover", this.onMouseOver, this);
1887         this.el.on("mouseout", this.onMouseOut, this);
1888         
1889         
1890     },
1891     findTargetItem : function(e){
1892         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1893         if(!t){
1894             return false;
1895         }
1896         //Roo.log(t);         Roo.log(t.id);
1897         if(t && t.id){
1898             //Roo.log(this.menuitems);
1899             return this.menuitems.get(t.id);
1900             
1901             //return this.items.get(t.menuItemId);
1902         }
1903         
1904         return false;
1905     },
1906     onClick : function(e){
1907         Roo.log("menu.onClick");
1908         var t = this.findTargetItem(e);
1909         if(!t || t.isContainer){
1910             return;
1911         }
1912         Roo.log(e);
1913         /*
1914         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1915             if(t == this.activeItem && t.shouldDeactivate(e)){
1916                 this.activeItem.deactivate();
1917                 delete this.activeItem;
1918                 return;
1919             }
1920             if(t.canActivate){
1921                 this.setActiveItem(t, true);
1922             }
1923             return;
1924             
1925             
1926         }
1927         */
1928        
1929         Roo.log('pass click event');
1930         
1931         t.onClick(e);
1932         
1933         this.fireEvent("click", this, t, e);
1934         
1935         this.hide();
1936     },
1937      onMouseOver : function(e){
1938         var t  = this.findTargetItem(e);
1939         //Roo.log(t);
1940         //if(t){
1941         //    if(t.canActivate && !t.disabled){
1942         //        this.setActiveItem(t, true);
1943         //    }
1944         //}
1945         
1946         this.fireEvent("mouseover", this, e, t);
1947     },
1948     isVisible : function(){
1949         return !this.hidden;
1950     },
1951      onMouseOut : function(e){
1952         var t  = this.findTargetItem(e);
1953         
1954         //if(t ){
1955         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1956         //        this.activeItem.deactivate();
1957         //        delete this.activeItem;
1958         //    }
1959         //}
1960         this.fireEvent("mouseout", this, e, t);
1961     },
1962     
1963     
1964     /**
1965      * Displays this menu relative to another element
1966      * @param {String/HTMLElement/Roo.Element} element The element to align to
1967      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1968      * the element (defaults to this.defaultAlign)
1969      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1970      */
1971     show : function(el, pos, parentMenu){
1972         this.parentMenu = parentMenu;
1973         if(!this.el){
1974             this.render();
1975         }
1976         this.fireEvent("beforeshow", this);
1977         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1978     },
1979      /**
1980      * Displays this menu at a specific xy position
1981      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1982      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1983      */
1984     showAt : function(xy, parentMenu, /* private: */_e){
1985         this.parentMenu = parentMenu;
1986         if(!this.el){
1987             this.render();
1988         }
1989         if(_e !== false){
1990             this.fireEvent("beforeshow", this);
1991             //xy = this.el.adjustForConstraints(xy);
1992         }
1993         
1994         //this.el.show();
1995         this.hideMenuItems();
1996         this.hidden = false;
1997         this.triggerEl.addClass('open');
1998         
1999         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2000             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2001         }
2002         
2003         this.el.setXY(xy);
2004         this.focus();
2005         this.fireEvent("show", this);
2006     },
2007     
2008     focus : function(){
2009         return;
2010         if(!this.hidden){
2011             this.doFocus.defer(50, this);
2012         }
2013     },
2014
2015     doFocus : function(){
2016         if(!this.hidden){
2017             this.focusEl.focus();
2018         }
2019     },
2020
2021     /**
2022      * Hides this menu and optionally all parent menus
2023      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2024      */
2025     hide : function(deep){
2026         
2027         this.hideMenuItems();
2028         if(this.el && this.isVisible()){
2029             this.fireEvent("beforehide", this);
2030             if(this.activeItem){
2031                 this.activeItem.deactivate();
2032                 this.activeItem = null;
2033             }
2034             this.triggerEl.removeClass('open');;
2035             this.hidden = true;
2036             this.fireEvent("hide", this);
2037         }
2038         if(deep === true && this.parentMenu){
2039             this.parentMenu.hide(true);
2040         }
2041     },
2042     
2043     onTriggerPress  : function(e)
2044     {
2045         
2046         Roo.log('trigger press');
2047         //Roo.log(e.getTarget());
2048        // Roo.log(this.triggerEl.dom);
2049         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2050             return;
2051         }
2052         
2053         if (this.isVisible()) {
2054             Roo.log('hide');
2055             this.hide();
2056         } else {
2057             Roo.log('show');
2058             this.show(this.triggerEl, false, false);
2059         }
2060         
2061         
2062     },
2063     
2064          
2065        
2066     
2067     hideMenuItems : function()
2068     {
2069         //$(backdrop).remove()
2070         Roo.select('.open',true).each(function(aa) {
2071             
2072             aa.removeClass('open');
2073           //var parent = getParent($(this))
2074           //var relatedTarget = { relatedTarget: this }
2075           
2076            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2077           //if (e.isDefaultPrevented()) return
2078            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2079         })
2080     },
2081     addxtypeChild : function (tree, cntr) {
2082         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2083           
2084         this.menuitems.add(comp);
2085         return comp;
2086
2087     },
2088     getEl : function()
2089     {
2090         Roo.log(this.el);
2091         return this.el;
2092     }
2093 });
2094
2095  
2096  /*
2097  * - LGPL
2098  *
2099  * menu item
2100  * 
2101  */
2102
2103
2104 /**
2105  * @class Roo.bootstrap.MenuItem
2106  * @extends Roo.bootstrap.Component
2107  * Bootstrap MenuItem class
2108  * @cfg {String} html the menu label
2109  * @cfg {String} href the link
2110  * @cfg {Boolean} preventDefault (true | false) default true
2111  * @cfg {Boolean} isContainer (true | false) default false
2112  * 
2113  * 
2114  * @constructor
2115  * Create a new MenuItem
2116  * @param {Object} config The config object
2117  */
2118
2119
2120 Roo.bootstrap.MenuItem = function(config){
2121     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2122     this.addEvents({
2123         // raw events
2124         /**
2125          * @event click
2126          * The raw click event for the entire grid.
2127          * @param {Roo.bootstrap.MenuItem} this
2128          * @param {Roo.EventObject} e
2129          */
2130         "click" : true
2131     });
2132 };
2133
2134 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2135     
2136     href : false,
2137     html : false,
2138     preventDefault: true,
2139     isContainer : false,
2140     
2141     getAutoCreate : function(){
2142         
2143         if(this.isContainer){
2144             return {
2145                 tag: 'li',
2146                 cls: 'dropdown-menu-item'
2147             };
2148         }
2149         
2150         var cfg= {
2151             tag: 'li',
2152             cls: 'dropdown-menu-item',
2153             cn: [
2154                     {
2155                         tag : 'a',
2156                         href : '#',
2157                         html : 'Link'
2158                     }
2159                 ]
2160         };
2161         if (this.parent().type == 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2166         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2167         return cfg;
2168     },
2169     
2170     initEvents: function() {
2171         
2172         //this.el.select('a').on('click', this.onClick, this);
2173         
2174     },
2175     onClick : function(e)
2176     {
2177         Roo.log('item on click ');
2178         //if(this.preventDefault){
2179         //    e.preventDefault();
2180         //}
2181         //this.parent().hideMenuItems();
2182         
2183         this.fireEvent('click', this, e);
2184     },
2185     getEl : function()
2186     {
2187         return this.el;
2188     }
2189 });
2190
2191  
2192
2193  /*
2194  * - LGPL
2195  *
2196  * menu separator
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuSeparator
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuSeparator class
2205  * 
2206  * @constructor
2207  * Create a new MenuItem
2208  * @param {Object} config The config object
2209  */
2210
2211
2212 Roo.bootstrap.MenuSeparator = function(config){
2213     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2214 };
2215
2216 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2217     
2218     getAutoCreate : function(){
2219         var cfg = {
2220             cls: 'divider',
2221             tag : 'li'
2222         };
2223         
2224         return cfg;
2225     }
2226    
2227 });
2228
2229  
2230
2231  
2232 /*
2233 * Licence: LGPL
2234 */
2235
2236 /**
2237  * @class Roo.bootstrap.Modal
2238  * @extends Roo.bootstrap.Component
2239  * Bootstrap Modal class
2240  * @cfg {String} title Title of dialog
2241  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2242  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2243  * @cfg {Boolean} specificTitle default false
2244  * @cfg {Array} buttons Array of buttons or standard button set..
2245  * @cfg {String} buttonPosition (left|right|center) default right
2246  * @cfg {Boolean} animate default true
2247  * @cfg {Boolean} allow_close default true
2248  * 
2249  * @constructor
2250  * Create a new Modal Dialog
2251  * @param {Object} config The config object
2252  */
2253
2254 Roo.bootstrap.Modal = function(config){
2255     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2256     this.addEvents({
2257         // raw events
2258         /**
2259          * @event btnclick
2260          * The raw btnclick event for the button
2261          * @param {Roo.EventObject} e
2262          */
2263         "btnclick" : true
2264     });
2265     this.buttons = this.buttons || [];
2266      
2267     if (this.tmpl) {
2268         this.tmpl = Roo.factory(this.tmpl);
2269     }
2270     
2271 };
2272
2273 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2274     
2275     title : 'test dialog',
2276    
2277     buttons : false,
2278     
2279     // set on load...
2280      
2281     html: false,
2282     
2283     tmp: false,
2284     
2285     specificTitle: false,
2286     
2287     buttonPosition: 'right',
2288     
2289     allow_close : true,
2290     
2291     animate : true,
2292     
2293     
2294      // private
2295     bodyEl:  false,
2296     footerEl:  false,
2297     titleEl:  false,
2298     closeEl:  false,
2299     
2300     
2301     onRender : function(ct, position)
2302     {
2303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2304      
2305         if(!this.el){
2306             var cfg = Roo.apply({},  this.getAutoCreate());
2307             cfg.id = Roo.id();
2308             //if(!cfg.name){
2309             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2310             //}
2311             //if (!cfg.name.length) {
2312             //    delete cfg.name;
2313            // }
2314             if (this.cls) {
2315                 cfg.cls += ' ' + this.cls;
2316             }
2317             if (this.style) {
2318                 cfg.style = this.style;
2319             }
2320             this.el = Roo.get(document.body).createChild(cfg, position);
2321         }
2322         //var type = this.el.dom.type;
2323         
2324         
2325         
2326         
2327         if(this.tabIndex !== undefined){
2328             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2329         }
2330         
2331         
2332         this.bodyEl = this.el.select('.modal-body',true).first();
2333         this.closeEl = this.el.select('.modal-header .close', true).first();
2334         this.footerEl = this.el.select('.modal-footer',true).first();
2335         this.titleEl = this.el.select('.modal-title',true).first();
2336         
2337         
2338          
2339         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2340         this.maskEl.enableDisplayMode("block");
2341         this.maskEl.hide();
2342         //this.el.addClass("x-dlg-modal");
2343     
2344         if (this.buttons.length) {
2345             Roo.each(this.buttons, function(bb) {
2346                 b = Roo.apply({}, bb);
2347                 b.xns = b.xns || Roo.bootstrap;
2348                 b.xtype = b.xtype || 'Button';
2349                 if (typeof(b.listeners) == 'undefined') {
2350                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2351                 }
2352                 
2353                 var btn = Roo.factory(b);
2354                 
2355                 btn.onRender(this.el.select('.modal-footer div').first());
2356                 
2357             },this);
2358         }
2359         // render the children.
2360         var nitems = [];
2361         
2362         if(typeof(this.items) != 'undefined'){
2363             var items = this.items;
2364             delete this.items;
2365
2366             for(var i =0;i < items.length;i++) {
2367                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2368             }
2369         }
2370         
2371         this.items = nitems;
2372         
2373         // where are these used - they used to be body/close/footer
2374         
2375        
2376         this.initEvents();
2377         //this.el.addClass([this.fieldClass, this.cls]);
2378         
2379     },
2380     getAutoCreate : function(){
2381         
2382         
2383         var bdy = {
2384                 cls : 'modal-body',
2385                 html : this.html || ''
2386         };
2387         
2388         var title = {
2389             tag: 'h4',
2390             cls : 'modal-title',
2391             html : this.title
2392         };
2393         
2394         if(this.specificTitle){
2395             title = this.title;
2396             
2397         };
2398         
2399         var header = [];
2400         if (this.allow_close) {
2401             header.push({
2402                 tag: 'button',
2403                 cls : 'close',
2404                 html : '&times'
2405             });
2406         }
2407         header.push(title);
2408         
2409         var modal = {
2410             cls: "modal",
2411             style : 'display: none',
2412             cn : [
2413                 {
2414                     cls: "modal-dialog",
2415                     cn : [
2416                         {
2417                             cls : "modal-content",
2418                             cn : [
2419                                 {
2420                                     cls : 'modal-header',
2421                                     cn : header
2422                                 },
2423                                 bdy,
2424                                 {
2425                                     cls : 'modal-footer',
2426                                     cn : [
2427                                         {
2428                                             tag: 'div',
2429                                             cls: 'btn-' + this.buttonPosition
2430                                         }
2431                                     ]
2432                                     
2433                                 }
2434                                 
2435                                 
2436                             ]
2437                             
2438                         }
2439                     ]
2440                         
2441                 }
2442             ]
2443         };
2444         
2445         if(this.animate){
2446             modal.cls += ' fade';
2447         }
2448         
2449         return modal;
2450           
2451     },
2452     getChildContainer : function() {
2453          
2454          return this.bodyEl;
2455         
2456     },
2457     getButtonContainer : function() {
2458          return this.el.select('.modal-footer div',true).first();
2459         
2460     },
2461     initEvents : function()
2462     {
2463         if (this.allow_close) {
2464             this.closeEl.on('click', this.hide, this);
2465         }
2466
2467     },
2468     show : function() {
2469         
2470         if (!this.rendered) {
2471             this.render();
2472         }
2473         
2474         this.el.setStyle('display', 'block');
2475         
2476         if(this.animate){
2477             var _this = this;
2478             (function(){ _this.el.addClass('in'); }).defer(50);
2479         }else{
2480             this.el.addClass('in');
2481         }
2482         
2483         // not sure how we can show data in here.. 
2484         //if (this.tmpl) {
2485         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2486         //}
2487         
2488         Roo.get(document.body).addClass("x-body-masked");
2489         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2490         this.maskEl.show();
2491         this.el.setStyle('zIndex', '10001');
2492        
2493         this.fireEvent('show', this);
2494         
2495         
2496     },
2497     hide : function()
2498     {
2499         this.maskEl.hide();
2500         Roo.get(document.body).removeClass("x-body-masked");
2501         this.el.removeClass('in');
2502         
2503         if(this.animate){
2504             var _this = this;
2505             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2506         }else{
2507             this.el.setStyle('display', 'none');
2508         }
2509         
2510         this.fireEvent('hide', this);
2511     },
2512     
2513     addButton : function(str, cb)
2514     {
2515          
2516         
2517         var b = Roo.apply({}, { html : str } );
2518         b.xns = b.xns || Roo.bootstrap;
2519         b.xtype = b.xtype || 'Button';
2520         if (typeof(b.listeners) == 'undefined') {
2521             b.listeners = { click : cb.createDelegate(this)  };
2522         }
2523         
2524         var btn = Roo.factory(b);
2525            
2526         btn.onRender(this.el.select('.modal-footer div').first());
2527         
2528         return btn;   
2529        
2530     },
2531     
2532     setDefaultButton : function(btn)
2533     {
2534         //this.el.select('.modal-footer').()
2535     },
2536     resizeTo: function(w,h)
2537     {
2538         // skip..
2539     },
2540     setContentSize  : function(w, h)
2541     {
2542         
2543     },
2544     onButtonClick: function(btn,e)
2545     {
2546         //Roo.log([a,b,c]);
2547         this.fireEvent('btnclick', btn.name, e);
2548     },
2549      /**
2550      * Set the title of the Dialog
2551      * @param {String} str new Title
2552      */
2553     setTitle: function(str) {
2554         this.titleEl.dom.innerHTML = str;    
2555     },
2556     /**
2557      * Set the body of the Dialog
2558      * @param {String} str new Title
2559      */
2560     setBody: function(str) {
2561         this.bodyEl.dom.innerHTML = str;    
2562     },
2563     /**
2564      * Set the body of the Dialog using the template
2565      * @param {Obj} data - apply this data to the template and replace the body contents.
2566      */
2567     applyBody: function(obj)
2568     {
2569         if (!this.tmpl) {
2570             Roo.log("Error - using apply Body without a template");
2571             //code
2572         }
2573         this.tmpl.overwrite(this.bodyEl, obj);
2574     }
2575     
2576 });
2577
2578
2579 Roo.apply(Roo.bootstrap.Modal,  {
2580     /**
2581          * Button config that displays a single OK button
2582          * @type Object
2583          */
2584         OK :  [{
2585             name : 'ok',
2586             weight : 'primary',
2587             html : 'OK'
2588         }], 
2589         /**
2590          * Button config that displays Yes and No buttons
2591          * @type Object
2592          */
2593         YESNO : [
2594             {
2595                 name  : 'no',
2596                 html : 'No'
2597             },
2598             {
2599                 name  :'yes',
2600                 weight : 'primary',
2601                 html : 'Yes'
2602             }
2603         ],
2604         
2605         /**
2606          * Button config that displays OK and Cancel buttons
2607          * @type Object
2608          */
2609         OKCANCEL : [
2610             {
2611                name : 'cancel',
2612                 html : 'Cancel'
2613             },
2614             {
2615                 name : 'ok',
2616                 weight : 'primary',
2617                 html : 'OK'
2618             }
2619         ],
2620         /**
2621          * Button config that displays Yes, No and Cancel buttons
2622          * @type Object
2623          */
2624         YESNOCANCEL : [
2625             {
2626                 name : 'yes',
2627                 weight : 'primary',
2628                 html : 'Yes'
2629             },
2630             {
2631                 name : 'no',
2632                 html : 'No'
2633             },
2634             {
2635                 name : 'cancel',
2636                 html : 'Cancel'
2637             }
2638         ]
2639 });
2640  
2641  /*
2642  * - LGPL
2643  *
2644  * messagebox - can be used as a replace
2645  * 
2646  */
2647 /**
2648  * @class Roo.MessageBox
2649  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2650  * Example usage:
2651  *<pre><code>
2652 // Basic alert:
2653 Roo.Msg.alert('Status', 'Changes saved successfully.');
2654
2655 // Prompt for user data:
2656 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2657     if (btn == 'ok'){
2658         // process text value...
2659     }
2660 });
2661
2662 // Show a dialog using config options:
2663 Roo.Msg.show({
2664    title:'Save Changes?',
2665    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2666    buttons: Roo.Msg.YESNOCANCEL,
2667    fn: processResult,
2668    animEl: 'elId'
2669 });
2670 </code></pre>
2671  * @singleton
2672  */
2673 Roo.bootstrap.MessageBox = function(){
2674     var dlg, opt, mask, waitTimer;
2675     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2676     var buttons, activeTextEl, bwidth;
2677
2678     
2679     // private
2680     var handleButton = function(button){
2681         dlg.hide();
2682         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2683     };
2684
2685     // private
2686     var handleHide = function(){
2687         if(opt && opt.cls){
2688             dlg.el.removeClass(opt.cls);
2689         }
2690         //if(waitTimer){
2691         //    Roo.TaskMgr.stop(waitTimer);
2692         //    waitTimer = null;
2693         //}
2694     };
2695
2696     // private
2697     var updateButtons = function(b){
2698         var width = 0;
2699         if(!b){
2700             buttons["ok"].hide();
2701             buttons["cancel"].hide();
2702             buttons["yes"].hide();
2703             buttons["no"].hide();
2704             //dlg.footer.dom.style.display = 'none';
2705             return width;
2706         }
2707         dlg.footerEl.dom.style.display = '';
2708         for(var k in buttons){
2709             if(typeof buttons[k] != "function"){
2710                 if(b[k]){
2711                     buttons[k].show();
2712                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2713                     width += buttons[k].el.getWidth()+15;
2714                 }else{
2715                     buttons[k].hide();
2716                 }
2717             }
2718         }
2719         return width;
2720     };
2721
2722     // private
2723     var handleEsc = function(d, k, e){
2724         if(opt && opt.closable !== false){
2725             dlg.hide();
2726         }
2727         if(e){
2728             e.stopEvent();
2729         }
2730     };
2731
2732     return {
2733         /**
2734          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2735          * @return {Roo.BasicDialog} The BasicDialog element
2736          */
2737         getDialog : function(){
2738            if(!dlg){
2739                 dlg = new Roo.bootstrap.Modal( {
2740                     //draggable: true,
2741                     //resizable:false,
2742                     //constraintoviewport:false,
2743                     //fixedcenter:true,
2744                     //collapsible : false,
2745                     //shim:true,
2746                     //modal: true,
2747                   //  width:400,
2748                   //  height:100,
2749                     //buttonAlign:"center",
2750                     closeClick : function(){
2751                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2752                             handleButton("no");
2753                         }else{
2754                             handleButton("cancel");
2755                         }
2756                     }
2757                 });
2758                 dlg.render();
2759                 dlg.on("hide", handleHide);
2760                 mask = dlg.mask;
2761                 //dlg.addKeyListener(27, handleEsc);
2762                 buttons = {};
2763                 this.buttons = buttons;
2764                 var bt = this.buttonText;
2765                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2766                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2767                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2768                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2769                 Roo.log(buttons)
2770                 bodyEl = dlg.bodyEl.createChild({
2771
2772                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2773                         '<textarea class="roo-mb-textarea"></textarea>' +
2774                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2775                 });
2776                 msgEl = bodyEl.dom.firstChild;
2777                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2778                 textboxEl.enableDisplayMode();
2779                 textboxEl.addKeyListener([10,13], function(){
2780                     if(dlg.isVisible() && opt && opt.buttons){
2781                         if(opt.buttons.ok){
2782                             handleButton("ok");
2783                         }else if(opt.buttons.yes){
2784                             handleButton("yes");
2785                         }
2786                     }
2787                 });
2788                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2789                 textareaEl.enableDisplayMode();
2790                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2791                 progressEl.enableDisplayMode();
2792                 var pf = progressEl.dom.firstChild;
2793                 if (pf) {
2794                     pp = Roo.get(pf.firstChild);
2795                     pp.setHeight(pf.offsetHeight);
2796                 }
2797                 
2798             }
2799             return dlg;
2800         },
2801
2802         /**
2803          * Updates the message box body text
2804          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2805          * the XHTML-compliant non-breaking space character '&amp;#160;')
2806          * @return {Roo.MessageBox} This message box
2807          */
2808         updateText : function(text){
2809             if(!dlg.isVisible() && !opt.width){
2810                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2811             }
2812             msgEl.innerHTML = text || '&#160;';
2813       
2814             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2815             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2816             var w = Math.max(
2817                     Math.min(opt.width || cw , this.maxWidth), 
2818                     Math.max(opt.minWidth || this.minWidth, bwidth)
2819             );
2820             if(opt.prompt){
2821                 activeTextEl.setWidth(w);
2822             }
2823             if(dlg.isVisible()){
2824                 dlg.fixedcenter = false;
2825             }
2826             // to big, make it scroll. = But as usual stupid IE does not support
2827             // !important..
2828             
2829             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2830                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2831                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2832             } else {
2833                 bodyEl.dom.style.height = '';
2834                 bodyEl.dom.style.overflowY = '';
2835             }
2836             if (cw > w) {
2837                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2838             } else {
2839                 bodyEl.dom.style.overflowX = '';
2840             }
2841             
2842             dlg.setContentSize(w, bodyEl.getHeight());
2843             if(dlg.isVisible()){
2844                 dlg.fixedcenter = true;
2845             }
2846             return this;
2847         },
2848
2849         /**
2850          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2851          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2852          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2853          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2854          * @return {Roo.MessageBox} This message box
2855          */
2856         updateProgress : function(value, text){
2857             if(text){
2858                 this.updateText(text);
2859             }
2860             if (pp) { // weird bug on my firefox - for some reason this is not defined
2861                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2862             }
2863             return this;
2864         },        
2865
2866         /**
2867          * Returns true if the message box is currently displayed
2868          * @return {Boolean} True if the message box is visible, else false
2869          */
2870         isVisible : function(){
2871             return dlg && dlg.isVisible();  
2872         },
2873
2874         /**
2875          * Hides the message box if it is displayed
2876          */
2877         hide : function(){
2878             if(this.isVisible()){
2879                 dlg.hide();
2880             }  
2881         },
2882
2883         /**
2884          * Displays a new message box, or reinitializes an existing message box, based on the config options
2885          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2886          * The following config object properties are supported:
2887          * <pre>
2888 Property    Type             Description
2889 ----------  ---------------  ------------------------------------------------------------------------------------
2890 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2891                                    closes (defaults to undefined)
2892 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2893                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2894 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2895                                    progress and wait dialogs will ignore this property and always hide the
2896                                    close button as they can only be closed programmatically.
2897 cls               String           A custom CSS class to apply to the message box element
2898 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2899                                    displayed (defaults to 75)
2900 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2901                                    function will be btn (the name of the button that was clicked, if applicable,
2902                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2903                                    Progress and wait dialogs will ignore this option since they do not respond to
2904                                    user actions and can only be closed programmatically, so any required function
2905                                    should be called by the same code after it closes the dialog.
2906 icon              String           A CSS class that provides a background image to be used as an icon for
2907                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2908 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2909 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2910 modal             Boolean          False to allow user interaction with the page while the message box is
2911                                    displayed (defaults to true)
2912 msg               String           A string that will replace the existing message box body text (defaults
2913                                    to the XHTML-compliant non-breaking space character '&#160;')
2914 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2915 progress          Boolean          True to display a progress bar (defaults to false)
2916 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2917 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2918 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2919 title             String           The title text
2920 value             String           The string value to set into the active textbox element if displayed
2921 wait              Boolean          True to display a progress bar (defaults to false)
2922 width             Number           The width of the dialog in pixels
2923 </pre>
2924          *
2925          * Example usage:
2926          * <pre><code>
2927 Roo.Msg.show({
2928    title: 'Address',
2929    msg: 'Please enter your address:',
2930    width: 300,
2931    buttons: Roo.MessageBox.OKCANCEL,
2932    multiline: true,
2933    fn: saveAddress,
2934    animEl: 'addAddressBtn'
2935 });
2936 </code></pre>
2937          * @param {Object} config Configuration options
2938          * @return {Roo.MessageBox} This message box
2939          */
2940         show : function(options)
2941         {
2942             
2943             // this causes nightmares if you show one dialog after another
2944             // especially on callbacks..
2945              
2946             if(this.isVisible()){
2947                 
2948                 this.hide();
2949                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2950                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2951                 Roo.log("New Dialog Message:" +  options.msg )
2952                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2953                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2954                 
2955             }
2956             var d = this.getDialog();
2957             opt = options;
2958             d.setTitle(opt.title || "&#160;");
2959             d.closeEl.setDisplayed(opt.closable !== false);
2960             activeTextEl = textboxEl;
2961             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2962             if(opt.prompt){
2963                 if(opt.multiline){
2964                     textboxEl.hide();
2965                     textareaEl.show();
2966                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2967                         opt.multiline : this.defaultTextHeight);
2968                     activeTextEl = textareaEl;
2969                 }else{
2970                     textboxEl.show();
2971                     textareaEl.hide();
2972                 }
2973             }else{
2974                 textboxEl.hide();
2975                 textareaEl.hide();
2976             }
2977             progressEl.setDisplayed(opt.progress === true);
2978             this.updateProgress(0);
2979             activeTextEl.dom.value = opt.value || "";
2980             if(opt.prompt){
2981                 dlg.setDefaultButton(activeTextEl);
2982             }else{
2983                 var bs = opt.buttons;
2984                 var db = null;
2985                 if(bs && bs.ok){
2986                     db = buttons["ok"];
2987                 }else if(bs && bs.yes){
2988                     db = buttons["yes"];
2989                 }
2990                 dlg.setDefaultButton(db);
2991             }
2992             bwidth = updateButtons(opt.buttons);
2993             this.updateText(opt.msg);
2994             if(opt.cls){
2995                 d.el.addClass(opt.cls);
2996             }
2997             d.proxyDrag = opt.proxyDrag === true;
2998             d.modal = opt.modal !== false;
2999             d.mask = opt.modal !== false ? mask : false;
3000             if(!d.isVisible()){
3001                 // force it to the end of the z-index stack so it gets a cursor in FF
3002                 document.body.appendChild(dlg.el.dom);
3003                 d.animateTarget = null;
3004                 d.show(options.animEl);
3005             }
3006             return this;
3007         },
3008
3009         /**
3010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3012          * and closing the message box when the process is complete.
3013          * @param {String} title The title bar text
3014          * @param {String} msg The message box body text
3015          * @return {Roo.MessageBox} This message box
3016          */
3017         progress : function(title, msg){
3018             this.show({
3019                 title : title,
3020                 msg : msg,
3021                 buttons: false,
3022                 progress:true,
3023                 closable:false,
3024                 minWidth: this.minProgressWidth,
3025                 modal : true
3026             });
3027             return this;
3028         },
3029
3030         /**
3031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3032          * If a callback function is passed it will be called after the user clicks the button, and the
3033          * id of the button that was clicked will be passed as the only parameter to the callback
3034          * (could also be the top-right close button).
3035          * @param {String} title The title bar text
3036          * @param {String} msg The message box body text
3037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3038          * @param {Object} scope (optional) The scope of the callback function
3039          * @return {Roo.MessageBox} This message box
3040          */
3041         alert : function(title, msg, fn, scope){
3042             this.show({
3043                 title : title,
3044                 msg : msg,
3045                 buttons: this.OK,
3046                 fn: fn,
3047                 scope : scope,
3048                 modal : true
3049             });
3050             return this;
3051         },
3052
3053         /**
3054          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3055          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3056          * You are responsible for closing the message box when the process is complete.
3057          * @param {String} msg The message box body text
3058          * @param {String} title (optional) The title bar text
3059          * @return {Roo.MessageBox} This message box
3060          */
3061         wait : function(msg, title){
3062             this.show({
3063                 title : title,
3064                 msg : msg,
3065                 buttons: false,
3066                 closable:false,
3067                 progress:true,
3068                 modal:true,
3069                 width:300,
3070                 wait:true
3071             });
3072             waitTimer = Roo.TaskMgr.start({
3073                 run: function(i){
3074                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3075                 },
3076                 interval: 1000
3077             });
3078             return this;
3079         },
3080
3081         /**
3082          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3083          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3084          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3085          * @param {String} title The title bar text
3086          * @param {String} msg The message box body text
3087          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3088          * @param {Object} scope (optional) The scope of the callback function
3089          * @return {Roo.MessageBox} This message box
3090          */
3091         confirm : function(title, msg, fn, scope){
3092             this.show({
3093                 title : title,
3094                 msg : msg,
3095                 buttons: this.YESNO,
3096                 fn: fn,
3097                 scope : scope,
3098                 modal : true
3099             });
3100             return this;
3101         },
3102
3103         /**
3104          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3105          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3106          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3107          * (could also be the top-right close button) and the text that was entered will be passed as the two
3108          * parameters to the callback.
3109          * @param {String} title The title bar text
3110          * @param {String} msg The message box body text
3111          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3112          * @param {Object} scope (optional) The scope of the callback function
3113          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3114          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3115          * @return {Roo.MessageBox} This message box
3116          */
3117         prompt : function(title, msg, fn, scope, multiline){
3118             this.show({
3119                 title : title,
3120                 msg : msg,
3121                 buttons: this.OKCANCEL,
3122                 fn: fn,
3123                 minWidth:250,
3124                 scope : scope,
3125                 prompt:true,
3126                 multiline: multiline,
3127                 modal : true
3128             });
3129             return this;
3130         },
3131
3132         /**
3133          * Button config that displays a single OK button
3134          * @type Object
3135          */
3136         OK : {ok:true},
3137         /**
3138          * Button config that displays Yes and No buttons
3139          * @type Object
3140          */
3141         YESNO : {yes:true, no:true},
3142         /**
3143          * Button config that displays OK and Cancel buttons
3144          * @type Object
3145          */
3146         OKCANCEL : {ok:true, cancel:true},
3147         /**
3148          * Button config that displays Yes, No and Cancel buttons
3149          * @type Object
3150          */
3151         YESNOCANCEL : {yes:true, no:true, cancel:true},
3152
3153         /**
3154          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3155          * @type Number
3156          */
3157         defaultTextHeight : 75,
3158         /**
3159          * The maximum width in pixels of the message box (defaults to 600)
3160          * @type Number
3161          */
3162         maxWidth : 600,
3163         /**
3164          * The minimum width in pixels of the message box (defaults to 100)
3165          * @type Number
3166          */
3167         minWidth : 100,
3168         /**
3169          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3170          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3171          * @type Number
3172          */
3173         minProgressWidth : 250,
3174         /**
3175          * An object containing the default button text strings that can be overriden for localized language support.
3176          * Supported properties are: ok, cancel, yes and no.
3177          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3178          * @type Object
3179          */
3180         buttonText : {
3181             ok : "OK",
3182             cancel : "Cancel",
3183             yes : "Yes",
3184             no : "No"
3185         }
3186     };
3187 }();
3188
3189 /**
3190  * Shorthand for {@link Roo.MessageBox}
3191  */
3192 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3193 Roo.Msg = Roo.Msg || Roo.MessageBox;
3194 /*
3195  * - LGPL
3196  *
3197  * navbar
3198  * 
3199  */
3200
3201 /**
3202  * @class Roo.bootstrap.Navbar
3203  * @extends Roo.bootstrap.Component
3204  * Bootstrap Navbar class
3205
3206  * @constructor
3207  * Create a new Navbar
3208  * @param {Object} config The config object
3209  */
3210
3211
3212 Roo.bootstrap.Navbar = function(config){
3213     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3214     
3215 };
3216
3217 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3218     
3219     
3220    
3221     // private
3222     navItems : false,
3223     loadMask : false,
3224     
3225     
3226     getAutoCreate : function(){
3227         
3228         
3229         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3230         
3231     },
3232     
3233     initEvents :function ()
3234     {
3235         //Roo.log(this.el.select('.navbar-toggle',true));
3236         this.el.select('.navbar-toggle',true).on('click', function() {
3237            // Roo.log('click');
3238             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3239         }, this);
3240         
3241         var mark = {
3242             tag: "div",
3243             cls:"x-dlg-mask"
3244         }
3245         
3246         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3247         
3248         var size = this.el.getSize();
3249         this.maskEl.setSize(size.width, size.height);
3250         this.maskEl.enableDisplayMode("block");
3251         this.maskEl.hide();
3252         
3253         if(this.loadMask){
3254             this.maskEl.show();
3255         }
3256     },
3257     
3258     
3259     getChildContainer : function()
3260     {
3261         if (this.el.select('.collapse').getCount()) {
3262             return this.el.select('.collapse',true).first();
3263         }
3264         
3265         return this.el;
3266     },
3267     
3268     mask : function()
3269     {
3270         this.maskEl.show();
3271     },
3272     
3273     unmask : function()
3274     {
3275         this.maskEl.hide();
3276     } 
3277     
3278     
3279     
3280     
3281 });
3282
3283
3284
3285  
3286
3287  /*
3288  * - LGPL
3289  *
3290  * navbar
3291  * 
3292  */
3293
3294 /**
3295  * @class Roo.bootstrap.NavSimplebar
3296  * @extends Roo.bootstrap.Navbar
3297  * Bootstrap Sidebar class
3298  *
3299  * @cfg {Boolean} inverse is inverted color
3300  * 
3301  * @cfg {String} type (nav | pills | tabs)
3302  * @cfg {Boolean} arrangement stacked | justified
3303  * @cfg {String} align (left | right) alignment
3304  * 
3305  * @cfg {Boolean} main (true|false) main nav bar? default false
3306  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3307  * 
3308  * @cfg {String} tag (header|footer|nav|div) default is nav 
3309
3310  * 
3311  * 
3312  * 
3313  * @constructor
3314  * Create a new Sidebar
3315  * @param {Object} config The config object
3316  */
3317
3318
3319 Roo.bootstrap.NavSimplebar = function(config){
3320     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3321 };
3322
3323 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3324     
3325     inverse: false,
3326     
3327     type: false,
3328     arrangement: '',
3329     align : false,
3330     
3331     
3332     
3333     main : false,
3334     
3335     
3336     tag : false,
3337     
3338     
3339     getAutoCreate : function(){
3340         
3341         
3342         var cfg = {
3343             tag : this.tag || 'div',
3344             cls : 'navbar'
3345         };
3346           
3347         
3348         cfg.cn = [
3349             {
3350                 cls: 'nav',
3351                 tag : 'ul'
3352             }
3353         ];
3354         
3355          
3356         this.type = this.type || 'nav';
3357         if (['tabs','pills'].indexOf(this.type)!==-1) {
3358             cfg.cn[0].cls += ' nav-' + this.type
3359         
3360         
3361         } else {
3362             if (this.type!=='nav') {
3363                 Roo.log('nav type must be nav/tabs/pills')
3364             }
3365             cfg.cn[0].cls += ' navbar-nav'
3366         }
3367         
3368         
3369         
3370         
3371         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3372             cfg.cn[0].cls += ' nav-' + this.arrangement;
3373         }
3374         
3375         
3376         if (this.align === 'right') {
3377             cfg.cn[0].cls += ' navbar-right';
3378         }
3379         
3380         if (this.inverse) {
3381             cfg.cls += ' navbar-inverse';
3382             
3383         }
3384         
3385         
3386         return cfg;
3387     
3388         
3389     }
3390     
3391     
3392     
3393 });
3394
3395
3396
3397  
3398
3399  
3400        /*
3401  * - LGPL
3402  *
3403  * navbar
3404  * 
3405  */
3406
3407 /**
3408  * @class Roo.bootstrap.NavHeaderbar
3409  * @extends Roo.bootstrap.NavSimplebar
3410  * Bootstrap Sidebar class
3411  *
3412  * @cfg {String} brand what is brand
3413  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3414  * @cfg {String} brand_href href of the brand
3415  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3416  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3417  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3418  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3419  * 
3420  * @constructor
3421  * Create a new Sidebar
3422  * @param {Object} config The config object
3423  */
3424
3425
3426 Roo.bootstrap.NavHeaderbar = function(config){
3427     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3428       
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3432     
3433     position: '',
3434     brand: '',
3435     brand_href: false,
3436     srButton : true,
3437     autohide : false,
3438     desktopCenter : false,
3439    
3440     
3441     getAutoCreate : function(){
3442         
3443         var   cfg = {
3444             tag: this.nav || 'nav',
3445             cls: 'navbar',
3446             role: 'navigation',
3447             cn: []
3448         };
3449         
3450         var cn = cfg.cn;
3451         if (this.desktopCenter) {
3452             cn.push({cls : 'container', cn : []});
3453             cn = cn[0].cn;
3454         }
3455         
3456         if(this.srButton){
3457             cn.push({
3458                 tag: 'div',
3459                 cls: 'navbar-header',
3460                 cn: [
3461                     {
3462                         tag: 'button',
3463                         type: 'button',
3464                         cls: 'navbar-toggle',
3465                         'data-toggle': 'collapse',
3466                         cn: [
3467                             {
3468                                 tag: 'span',
3469                                 cls: 'sr-only',
3470                                 html: 'Toggle navigation'
3471                             },
3472                             {
3473                                 tag: 'span',
3474                                 cls: 'icon-bar'
3475                             },
3476                             {
3477                                 tag: 'span',
3478                                 cls: 'icon-bar'
3479                             },
3480                             {
3481                                 tag: 'span',
3482                                 cls: 'icon-bar'
3483                             }
3484                         ]
3485                     }
3486                 ]
3487             });
3488         }
3489         
3490         cn.push({
3491             tag: 'div',
3492             cls: 'collapse navbar-collapse',
3493             cn : []
3494         });
3495         
3496         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3497         
3498         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3499             cfg.cls += ' navbar-' + this.position;
3500             
3501             // tag can override this..
3502             
3503             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3504         }
3505         
3506         if (this.brand !== '') {
3507             cn[0].cn.push({
3508                 tag: 'a',
3509                 href: this.brand_href ? this.brand_href : '#',
3510                 cls: 'navbar-brand',
3511                 cn: [
3512                 this.brand
3513                 ]
3514             });
3515         }
3516         
3517         if(this.main){
3518             cfg.cls += ' main-nav';
3519         }
3520         
3521         
3522         return cfg;
3523
3524         
3525     },
3526     getHeaderChildContainer : function()
3527     {
3528         if (this.el.select('.navbar-header').getCount()) {
3529             return this.el.select('.navbar-header',true).first();
3530         }
3531         
3532         return this.getChildContainer();
3533     },
3534     
3535     
3536     initEvents : function()
3537     {
3538         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3539         
3540         if (this.autohide) {
3541             
3542             var prevScroll = 0;
3543             var ft = this.el;
3544             
3545             Roo.get(document).on('scroll',function(e) {
3546                 var ns = Roo.get(document).getScroll().top;
3547                 var os = prevScroll;
3548                 prevScroll = ns;
3549                 
3550                 if(ns > os){
3551                     ft.removeClass('slideDown');
3552                     ft.addClass('slideUp');
3553                     return;
3554                 }
3555                 ft.removeClass('slideUp');
3556                 ft.addClass('slideDown');
3557                  
3558               
3559           },this);
3560         }
3561     }    
3562     
3563 });
3564
3565
3566
3567  
3568
3569  /*
3570  * - LGPL
3571  *
3572  * navbar
3573  * 
3574  */
3575
3576 /**
3577  * @class Roo.bootstrap.NavSidebar
3578  * @extends Roo.bootstrap.Navbar
3579  * Bootstrap Sidebar class
3580  * 
3581  * @constructor
3582  * Create a new Sidebar
3583  * @param {Object} config The config object
3584  */
3585
3586
3587 Roo.bootstrap.NavSidebar = function(config){
3588     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3589 };
3590
3591 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3592     
3593     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3594     
3595     getAutoCreate : function(){
3596         
3597         
3598         return  {
3599             tag: 'div',
3600             cls: 'sidebar sidebar-nav'
3601         };
3602     
3603         
3604     }
3605     
3606     
3607     
3608 });
3609
3610
3611
3612  
3613
3614  /*
3615  * - LGPL
3616  *
3617  * nav group
3618  * 
3619  */
3620
3621 /**
3622  * @class Roo.bootstrap.NavGroup
3623  * @extends Roo.bootstrap.Component
3624  * Bootstrap NavGroup class
3625  * @cfg {String} align left | right
3626  * @cfg {Boolean} inverse false | true
3627  * @cfg {String} type (nav|pills|tab) default nav
3628  * @cfg {String} navId - reference Id for navbar.
3629
3630  * 
3631  * @constructor
3632  * Create a new nav group
3633  * @param {Object} config The config object
3634  */
3635
3636 Roo.bootstrap.NavGroup = function(config){
3637     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3638     this.navItems = [];
3639    
3640     Roo.bootstrap.NavGroup.register(this);
3641      this.addEvents({
3642         /**
3643              * @event changed
3644              * Fires when the active item changes
3645              * @param {Roo.bootstrap.NavGroup} this
3646              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3647              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3648          */
3649         'changed': true
3650      });
3651     
3652 };
3653
3654 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3655     
3656     align: '',
3657     inverse: false,
3658     form: false,
3659     type: 'nav',
3660     navId : '',
3661     // private
3662     
3663     navItems : false, 
3664     
3665     getAutoCreate : function()
3666     {
3667         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3668         
3669         cfg = {
3670             tag : 'ul',
3671             cls: 'nav' 
3672         }
3673         
3674         if (['tabs','pills'].indexOf(this.type)!==-1) {
3675             cfg.cls += ' nav-' + this.type
3676         } else {
3677             if (this.type!=='nav') {
3678                 Roo.log('nav type must be nav/tabs/pills')
3679             }
3680             cfg.cls += ' navbar-nav'
3681         }
3682         
3683         if (this.parent().sidebar) {
3684             cfg = {
3685                 tag: 'ul',
3686                 cls: 'dashboard-menu sidebar-menu'
3687             }
3688             
3689             return cfg;
3690         }
3691         
3692         if (this.form === true) {
3693             cfg = {
3694                 tag: 'form',
3695                 cls: 'navbar-form'
3696             }
3697             
3698             if (this.align === 'right') {
3699                 cfg.cls += ' navbar-right';
3700             } else {
3701                 cfg.cls += ' navbar-left';
3702             }
3703         }
3704         
3705         if (this.align === 'right') {
3706             cfg.cls += ' navbar-right';
3707         }
3708         
3709         if (this.inverse) {
3710             cfg.cls += ' navbar-inverse';
3711             
3712         }
3713         
3714         
3715         return cfg;
3716     },
3717     /**
3718     * sets the active Navigation item
3719     * @param {Roo.bootstrap.NavItem} the new current navitem
3720     */
3721     setActiveItem : function(item)
3722     {
3723         var prev = false;
3724         Roo.each(this.navItems, function(v){
3725             if (v == item) {
3726                 return ;
3727             }
3728             if (v.isActive()) {
3729                 v.setActive(false, true);
3730                 prev = v;
3731                 
3732             }
3733             
3734         });
3735
3736         item.setActive(true, true);
3737         this.fireEvent('changed', this, item, prev);
3738         
3739         
3740     },
3741     /**
3742     * gets the active Navigation item
3743     * @return {Roo.bootstrap.NavItem} the current navitem
3744     */
3745     getActive : function()
3746     {
3747         
3748         var prev = false;
3749         Roo.each(this.navItems, function(v){
3750             
3751             if (v.isActive()) {
3752                 prev = v;
3753                 
3754             }
3755             
3756         });
3757         return prev;
3758     },
3759     
3760     indexOfNav : function()
3761     {
3762         
3763         var prev = false;
3764         Roo.each(this.navItems, function(v,i){
3765             
3766             if (v.isActive()) {
3767                 prev = i;
3768                 
3769             }
3770             
3771         });
3772         return prev;
3773     },
3774     /**
3775     * adds a Navigation item
3776     * @param {Roo.bootstrap.NavItem} the navitem to add
3777     */
3778     addItem : function(cfg)
3779     {
3780         var cn = new Roo.bootstrap.NavItem(cfg);
3781         this.register(cn);
3782         cn.parentId = this.id;
3783         cn.onRender(this.el, null);
3784         return cn;
3785     },
3786     /**
3787     * register a Navigation item
3788     * @param {Roo.bootstrap.NavItem} the navitem to add
3789     */
3790     register : function(item)
3791     {
3792         this.navItems.push( item);
3793         item.navId = this.navId;
3794     
3795     },
3796     
3797     /**
3798     * clear all the Navigation item
3799     */
3800    
3801     clearAll : function()
3802     {
3803         this.navItems = [];
3804         this.el.dom.innerHTML = '';
3805     },
3806     
3807     getNavItem: function(tabId)
3808     {
3809         var ret = false;
3810         Roo.each(this.navItems, function(e) {
3811             if (e.tabId == tabId) {
3812                ret =  e;
3813                return false;
3814             }
3815             return true;
3816             
3817         });
3818         return ret;
3819     },
3820     
3821     setActiveNext : function()
3822     {
3823         var i = this.indexOfNav(this.getActive());
3824         if (i > this.navItems.length) {
3825             return;
3826         }
3827         this.setActiveItem(this.navItems[i+1]);
3828     },
3829     setActivePrev : function()
3830     {
3831         var i = this.indexOfNav(this.getActive());
3832         if (i  < 1) {
3833             return;
3834         }
3835         this.setActiveItem(this.navItems[i-1]);
3836     },
3837     clearWasActive : function(except) {
3838         Roo.each(this.navItems, function(e) {
3839             if (e.tabId != except.tabId && e.was_active) {
3840                e.was_active = false;
3841                return false;
3842             }
3843             return true;
3844             
3845         });
3846     },
3847     getWasActive : function ()
3848     {
3849         var r = false;
3850         Roo.each(this.navItems, function(e) {
3851             if (e.was_active) {
3852                r = e;
3853                return false;
3854             }
3855             return true;
3856             
3857         });
3858         return r;
3859     }
3860     
3861     
3862 });
3863
3864  
3865 Roo.apply(Roo.bootstrap.NavGroup, {
3866     
3867     groups: {},
3868      /**
3869     * register a Navigation Group
3870     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3871     */
3872     register : function(navgrp)
3873     {
3874         this.groups[navgrp.navId] = navgrp;
3875         
3876     },
3877     /**
3878     * fetch a Navigation Group based on the navigation ID
3879     * @param {string} the navgroup to add
3880     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3881     */
3882     get: function(navId) {
3883         if (typeof(this.groups[navId]) == 'undefined') {
3884             return false;
3885             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3886         }
3887         return this.groups[navId] ;
3888     }
3889     
3890     
3891     
3892 });
3893
3894  /*
3895  * - LGPL
3896  *
3897  * row
3898  * 
3899  */
3900
3901 /**
3902  * @class Roo.bootstrap.NavItem
3903  * @extends Roo.bootstrap.Component
3904  * Bootstrap Navbar.NavItem class
3905  * @cfg {String} href  link to
3906  * @cfg {String} html content of button
3907  * @cfg {String} badge text inside badge
3908  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3909  * @cfg {String} glyphicon name of glyphicon
3910  * @cfg {String} icon name of font awesome icon
3911  * @cfg {Boolean} active Is item active
3912  * @cfg {Boolean} disabled Is item disabled
3913  
3914  * @cfg {Boolean} preventDefault (true | false) default false
3915  * @cfg {String} tabId the tab that this item activates.
3916  * @cfg {String} tagtype (a|span) render as a href or span?
3917  * @cfg {Boolean} animateRef (true|false) link to element default false  
3918   
3919  * @constructor
3920  * Create a new Navbar Item
3921  * @param {Object} config The config object
3922  */
3923 Roo.bootstrap.NavItem = function(config){
3924     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3925     this.addEvents({
3926         // raw events
3927         /**
3928          * @event click
3929          * The raw click event for the entire grid.
3930          * @param {Roo.EventObject} e
3931          */
3932         "click" : true,
3933          /**
3934             * @event changed
3935             * Fires when the active item active state changes
3936             * @param {Roo.bootstrap.NavItem} this
3937             * @param {boolean} state the new state
3938              
3939          */
3940         'changed': true,
3941         /**
3942             * @event scrollto
3943             * Fires when scroll to element
3944             * @param {Roo.bootstrap.NavItem} this
3945             * @param {Object} options
3946             * @param {Roo.EventObject} e
3947              
3948          */
3949         'scrollto': true
3950     });
3951    
3952 };
3953
3954 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3955     
3956     href: false,
3957     html: '',
3958     badge: '',
3959     icon: false,
3960     glyphicon: false,
3961     active: false,
3962     preventDefault : false,
3963     tabId : false,
3964     tagtype : 'a',
3965     disabled : false,
3966     animateRef : false,
3967     was_active : false,
3968     
3969     getAutoCreate : function(){
3970          
3971         var cfg = {
3972             tag: 'li',
3973             cls: 'nav-item'
3974             
3975         }
3976         if (this.active) {
3977             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3978         }
3979         if (this.disabled) {
3980             cfg.cls += ' disabled';
3981         }
3982         
3983         if (this.href || this.html || this.glyphicon || this.icon) {
3984             cfg.cn = [
3985                 {
3986                     tag: this.tagtype,
3987                     href : this.href || "#",
3988                     html: this.html || ''
3989                 }
3990             ];
3991             
3992             if (this.icon) {
3993                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3994             }
3995
3996             if(this.glyphicon) {
3997                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3998             }
3999             
4000             if (this.menu) {
4001                 
4002                 cfg.cn[0].html += " <span class='caret'></span>";
4003              
4004             }
4005             
4006             if (this.badge !== '') {
4007                  
4008                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4009             }
4010         }
4011         
4012         
4013         
4014         return cfg;
4015     },
4016     initEvents: function() 
4017     {
4018         if (typeof (this.menu) != 'undefined') {
4019             this.menu.parentType = this.xtype;
4020             this.menu.triggerEl = this.el;
4021             this.menu = this.addxtype(Roo.apply({}, this.menu));
4022         }
4023         
4024         this.el.select('a',true).on('click', this.onClick, this);
4025         
4026         if(this.tagtype == 'span'){
4027             this.el.select('span',true).on('click', this.onClick, this);
4028         }
4029        
4030         // at this point parent should be available..
4031         this.parent().register(this);
4032     },
4033     
4034     onClick : function(e)
4035     {
4036         if(
4037                 this.preventDefault || 
4038                 this.href == '#' 
4039         ){
4040             
4041             e.preventDefault();
4042         }
4043         
4044         if (this.disabled) {
4045             return;
4046         }
4047         
4048         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4049         if (tg && tg.transition) {
4050             Roo.log("waiting for the transitionend");
4051             return;
4052         }
4053         
4054         
4055         
4056         //Roo.log("fire event clicked");
4057         if(this.fireEvent('click', this, e) === false){
4058             return;
4059         };
4060         
4061         if(this.tagtype == 'span'){
4062             return;
4063         }
4064         
4065         //Roo.log(this.href);
4066         var ael = this.el.select('a',true).first();
4067         //Roo.log(ael);
4068         
4069         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4070             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4071             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4072                 return; // ignore... - it's a 'hash' to another page.
4073             }
4074             
4075             e.preventDefault();
4076             this.scrollToElement(e);
4077         }
4078         
4079         
4080         var p =  this.parent();
4081    
4082         if (['tabs','pills'].indexOf(p.type)!==-1) {
4083             if (typeof(p.setActiveItem) !== 'undefined') {
4084                 p.setActiveItem(this);
4085             }
4086         }
4087         
4088         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4089         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4090             // remove the collapsed menu expand...
4091             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4092         }
4093     },
4094     
4095     isActive: function () {
4096         return this.active
4097     },
4098     setActive : function(state, fire, is_was_active)
4099     {
4100         if (this.active && !state & this.navId) {
4101             this.was_active = true;
4102             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4103             if (nv) {
4104                 nv.clearWasActive(this);
4105             }
4106             
4107         }
4108         this.active = state;
4109         
4110         if (!state ) {
4111             this.el.removeClass('active');
4112         } else if (!this.el.hasClass('active')) {
4113             this.el.addClass('active');
4114         }
4115         if (fire) {
4116             this.fireEvent('changed', this, state);
4117         }
4118         
4119         // show a panel if it's registered and related..
4120         
4121         if (!this.navId || !this.tabId || !state || is_was_active) {
4122             return;
4123         }
4124         
4125         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4126         if (!tg) {
4127             return;
4128         }
4129         var pan = tg.getPanelByName(this.tabId);
4130         if (!pan) {
4131             return;
4132         }
4133         // if we can not flip to new panel - go back to old nav highlight..
4134         if (false == tg.showPanel(pan)) {
4135             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4136             if (nv) {
4137                 var onav = nv.getWasActive();
4138                 if (onav) {
4139                     onav.setActive(true, false, true);
4140                 }
4141             }
4142             
4143         }
4144         
4145         
4146         
4147     },
4148      // this should not be here...
4149     setDisabled : function(state)
4150     {
4151         this.disabled = state;
4152         if (!state ) {
4153             this.el.removeClass('disabled');
4154         } else if (!this.el.hasClass('disabled')) {
4155             this.el.addClass('disabled');
4156         }
4157         
4158     },
4159     
4160     /**
4161      * Fetch the element to display the tooltip on.
4162      * @return {Roo.Element} defaults to this.el
4163      */
4164     tooltipEl : function()
4165     {
4166         return this.el.select('' + this.tagtype + '', true).first();
4167     },
4168     
4169     scrollToElement : function(e)
4170     {
4171         var c = document.body;
4172         
4173         /*
4174          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4175          */
4176         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4177             c = document.documentElement;
4178         }
4179         
4180         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4181         
4182         if(!target){
4183             return;
4184         }
4185
4186         var o = target.calcOffsetsTo(c);
4187         
4188         var options = {
4189             target : target,
4190             value : o[1]
4191         }
4192         
4193         this.fireEvent('scrollto', this, options, e);
4194         
4195         Roo.get(c).scrollTo('top', options.value, true);
4196         
4197         return;
4198     }
4199 });
4200  
4201
4202  /*
4203  * - LGPL
4204  *
4205  * sidebar item
4206  *
4207  *  li
4208  *    <span> icon </span>
4209  *    <span> text </span>
4210  *    <span>badge </span>
4211  */
4212
4213 /**
4214  * @class Roo.bootstrap.NavSidebarItem
4215  * @extends Roo.bootstrap.NavItem
4216  * Bootstrap Navbar.NavSidebarItem class
4217  * @constructor
4218  * Create a new Navbar Button
4219  * @param {Object} config The config object
4220  */
4221 Roo.bootstrap.NavSidebarItem = function(config){
4222     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4223     this.addEvents({
4224         // raw events
4225         /**
4226          * @event click
4227          * The raw click event for the entire grid.
4228          * @param {Roo.EventObject} e
4229          */
4230         "click" : true,
4231          /**
4232             * @event changed
4233             * Fires when the active item active state changes
4234             * @param {Roo.bootstrap.NavSidebarItem} this
4235             * @param {boolean} state the new state
4236              
4237          */
4238         'changed': true
4239     });
4240    
4241 };
4242
4243 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4244     
4245     
4246     getAutoCreate : function(){
4247         
4248         
4249         var a = {
4250                 tag: 'a',
4251                 href : this.href || '#',
4252                 cls: '',
4253                 html : '',
4254                 cn : []
4255         };
4256         var cfg = {
4257             tag: 'li',
4258             cls: '',
4259             cn: [ a ]
4260         }
4261         var span = {
4262             tag: 'span',
4263             html : this.html || ''
4264         }
4265         
4266         
4267         if (this.active) {
4268             cfg.cls += ' active';
4269         }
4270         
4271         // left icon..
4272         if (this.glyphicon || this.icon) {
4273             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4274             a.cn.push({ tag : 'i', cls : c }) ;
4275         }
4276         // html..
4277         a.cn.push(span);
4278         // then badge..
4279         if (this.badge !== '') {
4280             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4281         }
4282         // fi
4283         if (this.menu) {
4284             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4285             a.cls += 'dropdown-toggle treeview' ;
4286             
4287         }
4288         
4289         
4290         
4291         return cfg;
4292          
4293            
4294     }
4295    
4296      
4297  
4298 });
4299  
4300
4301  /*
4302  * - LGPL
4303  *
4304  * row
4305  * 
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.Row
4310  * @extends Roo.bootstrap.Component
4311  * Bootstrap Row class (contains columns...)
4312  * 
4313  * @constructor
4314  * Create a new Row
4315  * @param {Object} config The config object
4316  */
4317
4318 Roo.bootstrap.Row = function(config){
4319     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4320 };
4321
4322 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4323     
4324     getAutoCreate : function(){
4325        return {
4326             cls: 'row clearfix'
4327        };
4328     }
4329     
4330     
4331 });
4332
4333  
4334
4335  /*
4336  * - LGPL
4337  *
4338  * element
4339  * 
4340  */
4341
4342 /**
4343  * @class Roo.bootstrap.Element
4344  * @extends Roo.bootstrap.Component
4345  * Bootstrap Element class
4346  * @cfg {String} html contents of the element
4347  * @cfg {String} tag tag of the element
4348  * @cfg {String} cls class of the element
4349  * @cfg {Boolean} preventDefault (true|false) default false
4350  * @cfg {Boolean} clickable (true|false) default false
4351  * 
4352  * @constructor
4353  * Create a new Element
4354  * @param {Object} config The config object
4355  */
4356
4357 Roo.bootstrap.Element = function(config){
4358     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4359     
4360     this.addEvents({
4361         // raw events
4362         /**
4363          * @event click
4364          * When a element is chick
4365          * @param {Roo.bootstrap.Element} this
4366          * @param {Roo.EventObject} e
4367          */
4368         "click" : true
4369     });
4370 };
4371
4372 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4373     
4374     tag: 'div',
4375     cls: '',
4376     html: '',
4377     preventDefault: false, 
4378     clickable: false,
4379     
4380     getAutoCreate : function(){
4381         
4382         var cfg = {
4383             tag: this.tag,
4384             cls: this.cls,
4385             html: this.html
4386         }
4387         
4388         return cfg;
4389     },
4390     
4391     initEvents: function() 
4392     {
4393         Roo.bootstrap.Element.superclass.initEvents.call(this);
4394         
4395         if(this.clickable){
4396             this.el.on('click', this.onClick, this);
4397         }
4398         
4399     },
4400     
4401     onClick : function(e)
4402     {
4403         if(this.preventDefault){
4404             e.preventDefault();
4405         }
4406         
4407         this.fireEvent('click', this, e);
4408     },
4409     
4410     getValue : function()
4411     {
4412         return this.el.dom.innerHTML;
4413     },
4414     
4415     setValue : function(value)
4416     {
4417         this.el.dom.innerHTML = value;
4418     }
4419    
4420 });
4421
4422  
4423
4424  /*
4425  * - LGPL
4426  *
4427  * pagination
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.Pagination
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Pagination class
4435  * @cfg {String} size xs | sm | md | lg
4436  * @cfg {Boolean} inverse false | true
4437  * 
4438  * @constructor
4439  * Create a new Pagination
4440  * @param {Object} config The config object
4441  */
4442
4443 Roo.bootstrap.Pagination = function(config){
4444     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4445 };
4446
4447 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4448     
4449     cls: false,
4450     size: false,
4451     inverse: false,
4452     
4453     getAutoCreate : function(){
4454         var cfg = {
4455             tag: 'ul',
4456                 cls: 'pagination'
4457         };
4458         if (this.inverse) {
4459             cfg.cls += ' inverse';
4460         }
4461         if (this.html) {
4462             cfg.html=this.html;
4463         }
4464         if (this.cls) {
4465             cfg.cls += " " + this.cls;
4466         }
4467         return cfg;
4468     }
4469    
4470 });
4471
4472  
4473
4474  /*
4475  * - LGPL
4476  *
4477  * Pagination item
4478  * 
4479  */
4480
4481
4482 /**
4483  * @class Roo.bootstrap.PaginationItem
4484  * @extends Roo.bootstrap.Component
4485  * Bootstrap PaginationItem class
4486  * @cfg {String} html text
4487  * @cfg {String} href the link
4488  * @cfg {Boolean} preventDefault (true | false) default true
4489  * @cfg {Boolean} active (true | false) default false
4490  * @cfg {Boolean} disabled default false
4491  * 
4492  * 
4493  * @constructor
4494  * Create a new PaginationItem
4495  * @param {Object} config The config object
4496  */
4497
4498
4499 Roo.bootstrap.PaginationItem = function(config){
4500     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4501     this.addEvents({
4502         // raw events
4503         /**
4504          * @event click
4505          * The raw click event for the entire grid.
4506          * @param {Roo.EventObject} e
4507          */
4508         "click" : true
4509     });
4510 };
4511
4512 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4513     
4514     href : false,
4515     html : false,
4516     preventDefault: true,
4517     active : false,
4518     cls : false,
4519     disabled: false,
4520     
4521     getAutoCreate : function(){
4522         var cfg= {
4523             tag: 'li',
4524             cn: [
4525                 {
4526                     tag : 'a',
4527                     href : this.href ? this.href : '#',
4528                     html : this.html ? this.html : ''
4529                 }
4530             ]
4531         };
4532         
4533         if(this.cls){
4534             cfg.cls = this.cls;
4535         }
4536         
4537         if(this.disabled){
4538             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4539         }
4540         
4541         if(this.active){
4542             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4543         }
4544         
4545         return cfg;
4546     },
4547     
4548     initEvents: function() {
4549         
4550         this.el.on('click', this.onClick, this);
4551         
4552     },
4553     onClick : function(e)
4554     {
4555         Roo.log('PaginationItem on click ');
4556         if(this.preventDefault){
4557             e.preventDefault();
4558         }
4559         
4560         if(this.disabled){
4561             return;
4562         }
4563         
4564         this.fireEvent('click', this, e);
4565     }
4566    
4567 });
4568
4569  
4570
4571  /*
4572  * - LGPL
4573  *
4574  * slider
4575  * 
4576  */
4577
4578
4579 /**
4580  * @class Roo.bootstrap.Slider
4581  * @extends Roo.bootstrap.Component
4582  * Bootstrap Slider class
4583  *    
4584  * @constructor
4585  * Create a new Slider
4586  * @param {Object} config The config object
4587  */
4588
4589 Roo.bootstrap.Slider = function(config){
4590     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4591 };
4592
4593 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4594     
4595     getAutoCreate : function(){
4596         
4597         var cfg = {
4598             tag: 'div',
4599             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4600             cn: [
4601                 {
4602                     tag: 'a',
4603                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4604                 }
4605             ]
4606         }
4607         
4608         return cfg;
4609     }
4610    
4611 });
4612
4613  /*
4614  * Based on:
4615  * Ext JS Library 1.1.1
4616  * Copyright(c) 2006-2007, Ext JS, LLC.
4617  *
4618  * Originally Released Under LGPL - original licence link has changed is not relivant.
4619  *
4620  * Fork - LGPL
4621  * <script type="text/javascript">
4622  */
4623  
4624
4625 /**
4626  * @class Roo.grid.ColumnModel
4627  * @extends Roo.util.Observable
4628  * This is the default implementation of a ColumnModel used by the Grid. It defines
4629  * the columns in the grid.
4630  * <br>Usage:<br>
4631  <pre><code>
4632  var colModel = new Roo.grid.ColumnModel([
4633         {header: "Ticker", width: 60, sortable: true, locked: true},
4634         {header: "Company Name", width: 150, sortable: true},
4635         {header: "Market Cap.", width: 100, sortable: true},
4636         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4637         {header: "Employees", width: 100, sortable: true, resizable: false}
4638  ]);
4639  </code></pre>
4640  * <p>
4641  
4642  * The config options listed for this class are options which may appear in each
4643  * individual column definition.
4644  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4645  * @constructor
4646  * @param {Object} config An Array of column config objects. See this class's
4647  * config objects for details.
4648 */
4649 Roo.grid.ColumnModel = function(config){
4650         /**
4651      * The config passed into the constructor
4652      */
4653     this.config = config;
4654     this.lookup = {};
4655
4656     // if no id, create one
4657     // if the column does not have a dataIndex mapping,
4658     // map it to the order it is in the config
4659     for(var i = 0, len = config.length; i < len; i++){
4660         var c = config[i];
4661         if(typeof c.dataIndex == "undefined"){
4662             c.dataIndex = i;
4663         }
4664         if(typeof c.renderer == "string"){
4665             c.renderer = Roo.util.Format[c.renderer];
4666         }
4667         if(typeof c.id == "undefined"){
4668             c.id = Roo.id();
4669         }
4670         if(c.editor && c.editor.xtype){
4671             c.editor  = Roo.factory(c.editor, Roo.grid);
4672         }
4673         if(c.editor && c.editor.isFormField){
4674             c.editor = new Roo.grid.GridEditor(c.editor);
4675         }
4676         this.lookup[c.id] = c;
4677     }
4678
4679     /**
4680      * The width of columns which have no width specified (defaults to 100)
4681      * @type Number
4682      */
4683     this.defaultWidth = 100;
4684
4685     /**
4686      * Default sortable of columns which have no sortable specified (defaults to false)
4687      * @type Boolean
4688      */
4689     this.defaultSortable = false;
4690
4691     this.addEvents({
4692         /**
4693              * @event widthchange
4694              * Fires when the width of a column changes.
4695              * @param {ColumnModel} this
4696              * @param {Number} columnIndex The column index
4697              * @param {Number} newWidth The new width
4698              */
4699             "widthchange": true,
4700         /**
4701              * @event headerchange
4702              * Fires when the text of a header changes.
4703              * @param {ColumnModel} this
4704              * @param {Number} columnIndex The column index
4705              * @param {Number} newText The new header text
4706              */
4707             "headerchange": true,
4708         /**
4709              * @event hiddenchange
4710              * Fires when a column is hidden or "unhidden".
4711              * @param {ColumnModel} this
4712              * @param {Number} columnIndex The column index
4713              * @param {Boolean} hidden true if hidden, false otherwise
4714              */
4715             "hiddenchange": true,
4716             /**
4717          * @event columnmoved
4718          * Fires when a column is moved.
4719          * @param {ColumnModel} this
4720          * @param {Number} oldIndex
4721          * @param {Number} newIndex
4722          */
4723         "columnmoved" : true,
4724         /**
4725          * @event columlockchange
4726          * Fires when a column's locked state is changed
4727          * @param {ColumnModel} this
4728          * @param {Number} colIndex
4729          * @param {Boolean} locked true if locked
4730          */
4731         "columnlockchange" : true
4732     });
4733     Roo.grid.ColumnModel.superclass.constructor.call(this);
4734 };
4735 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4736     /**
4737      * @cfg {String} header The header text to display in the Grid view.
4738      */
4739     /**
4740      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4741      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4742      * specified, the column's index is used as an index into the Record's data Array.
4743      */
4744     /**
4745      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4746      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4747      */
4748     /**
4749      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4750      * Defaults to the value of the {@link #defaultSortable} property.
4751      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4752      */
4753     /**
4754      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4755      */
4756     /**
4757      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4758      */
4759     /**
4760      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4761      */
4762     /**
4763      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4764      */
4765     /**
4766      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4767      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4768      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4769      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4770      */
4771        /**
4772      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4773      */
4774     /**
4775      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4776      */
4777     /**
4778      * @cfg {String} cursor (Optional)
4779      */
4780     /**
4781      * @cfg {String} tooltip (Optional)
4782      */
4783     /**
4784      * Returns the id of the column at the specified index.
4785      * @param {Number} index The column index
4786      * @return {String} the id
4787      */
4788     getColumnId : function(index){
4789         return this.config[index].id;
4790     },
4791
4792     /**
4793      * Returns the column for a specified id.
4794      * @param {String} id The column id
4795      * @return {Object} the column
4796      */
4797     getColumnById : function(id){
4798         return this.lookup[id];
4799     },
4800
4801     
4802     /**
4803      * Returns the column for a specified dataIndex.
4804      * @param {String} dataIndex The column dataIndex
4805      * @return {Object|Boolean} the column or false if not found
4806      */
4807     getColumnByDataIndex: function(dataIndex){
4808         var index = this.findColumnIndex(dataIndex);
4809         return index > -1 ? this.config[index] : false;
4810     },
4811     
4812     /**
4813      * Returns the index for a specified column id.
4814      * @param {String} id The column id
4815      * @return {Number} the index, or -1 if not found
4816      */
4817     getIndexById : function(id){
4818         for(var i = 0, len = this.config.length; i < len; i++){
4819             if(this.config[i].id == id){
4820                 return i;
4821             }
4822         }
4823         return -1;
4824     },
4825     
4826     /**
4827      * Returns the index for a specified column dataIndex.
4828      * @param {String} dataIndex The column dataIndex
4829      * @return {Number} the index, or -1 if not found
4830      */
4831     
4832     findColumnIndex : function(dataIndex){
4833         for(var i = 0, len = this.config.length; i < len; i++){
4834             if(this.config[i].dataIndex == dataIndex){
4835                 return i;
4836             }
4837         }
4838         return -1;
4839     },
4840     
4841     
4842     moveColumn : function(oldIndex, newIndex){
4843         var c = this.config[oldIndex];
4844         this.config.splice(oldIndex, 1);
4845         this.config.splice(newIndex, 0, c);
4846         this.dataMap = null;
4847         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4848     },
4849
4850     isLocked : function(colIndex){
4851         return this.config[colIndex].locked === true;
4852     },
4853
4854     setLocked : function(colIndex, value, suppressEvent){
4855         if(this.isLocked(colIndex) == value){
4856             return;
4857         }
4858         this.config[colIndex].locked = value;
4859         if(!suppressEvent){
4860             this.fireEvent("columnlockchange", this, colIndex, value);
4861         }
4862     },
4863
4864     getTotalLockedWidth : function(){
4865         var totalWidth = 0;
4866         for(var i = 0; i < this.config.length; i++){
4867             if(this.isLocked(i) && !this.isHidden(i)){
4868                 this.totalWidth += this.getColumnWidth(i);
4869             }
4870         }
4871         return totalWidth;
4872     },
4873
4874     getLockedCount : function(){
4875         for(var i = 0, len = this.config.length; i < len; i++){
4876             if(!this.isLocked(i)){
4877                 return i;
4878             }
4879         }
4880     },
4881
4882     /**
4883      * Returns the number of columns.
4884      * @return {Number}
4885      */
4886     getColumnCount : function(visibleOnly){
4887         if(visibleOnly === true){
4888             var c = 0;
4889             for(var i = 0, len = this.config.length; i < len; i++){
4890                 if(!this.isHidden(i)){
4891                     c++;
4892                 }
4893             }
4894             return c;
4895         }
4896         return this.config.length;
4897     },
4898
4899     /**
4900      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4901      * @param {Function} fn
4902      * @param {Object} scope (optional)
4903      * @return {Array} result
4904      */
4905     getColumnsBy : function(fn, scope){
4906         var r = [];
4907         for(var i = 0, len = this.config.length; i < len; i++){
4908             var c = this.config[i];
4909             if(fn.call(scope||this, c, i) === true){
4910                 r[r.length] = c;
4911             }
4912         }
4913         return r;
4914     },
4915
4916     /**
4917      * Returns true if the specified column is sortable.
4918      * @param {Number} col The column index
4919      * @return {Boolean}
4920      */
4921     isSortable : function(col){
4922         if(typeof this.config[col].sortable == "undefined"){
4923             return this.defaultSortable;
4924         }
4925         return this.config[col].sortable;
4926     },
4927
4928     /**
4929      * Returns the rendering (formatting) function defined for the column.
4930      * @param {Number} col The column index.
4931      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4932      */
4933     getRenderer : function(col){
4934         if(!this.config[col].renderer){
4935             return Roo.grid.ColumnModel.defaultRenderer;
4936         }
4937         return this.config[col].renderer;
4938     },
4939
4940     /**
4941      * Sets the rendering (formatting) function for a column.
4942      * @param {Number} col The column index
4943      * @param {Function} fn The function to use to process the cell's raw data
4944      * to return HTML markup for the grid view. The render function is called with
4945      * the following parameters:<ul>
4946      * <li>Data value.</li>
4947      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4948      * <li>css A CSS style string to apply to the table cell.</li>
4949      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4950      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4951      * <li>Row index</li>
4952      * <li>Column index</li>
4953      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4954      */
4955     setRenderer : function(col, fn){
4956         this.config[col].renderer = fn;
4957     },
4958
4959     /**
4960      * Returns the width for the specified column.
4961      * @param {Number} col The column index
4962      * @return {Number}
4963      */
4964     getColumnWidth : function(col){
4965         return this.config[col].width * 1 || this.defaultWidth;
4966     },
4967
4968     /**
4969      * Sets the width for a column.
4970      * @param {Number} col The column index
4971      * @param {Number} width The new width
4972      */
4973     setColumnWidth : function(col, width, suppressEvent){
4974         this.config[col].width = width;
4975         this.totalWidth = null;
4976         if(!suppressEvent){
4977              this.fireEvent("widthchange", this, col, width);
4978         }
4979     },
4980
4981     /**
4982      * Returns the total width of all columns.
4983      * @param {Boolean} includeHidden True to include hidden column widths
4984      * @return {Number}
4985      */
4986     getTotalWidth : function(includeHidden){
4987         if(!this.totalWidth){
4988             this.totalWidth = 0;
4989             for(var i = 0, len = this.config.length; i < len; i++){
4990                 if(includeHidden || !this.isHidden(i)){
4991                     this.totalWidth += this.getColumnWidth(i);
4992                 }
4993             }
4994         }
4995         return this.totalWidth;
4996     },
4997
4998     /**
4999      * Returns the header for the specified column.
5000      * @param {Number} col The column index
5001      * @return {String}
5002      */
5003     getColumnHeader : function(col){
5004         return this.config[col].header;
5005     },
5006
5007     /**
5008      * Sets the header for a column.
5009      * @param {Number} col The column index
5010      * @param {String} header The new header
5011      */
5012     setColumnHeader : function(col, header){
5013         this.config[col].header = header;
5014         this.fireEvent("headerchange", this, col, header);
5015     },
5016
5017     /**
5018      * Returns the tooltip for the specified column.
5019      * @param {Number} col The column index
5020      * @return {String}
5021      */
5022     getColumnTooltip : function(col){
5023             return this.config[col].tooltip;
5024     },
5025     /**
5026      * Sets the tooltip for a column.
5027      * @param {Number} col The column index
5028      * @param {String} tooltip The new tooltip
5029      */
5030     setColumnTooltip : function(col, tooltip){
5031             this.config[col].tooltip = tooltip;
5032     },
5033
5034     /**
5035      * Returns the dataIndex for the specified column.
5036      * @param {Number} col The column index
5037      * @return {Number}
5038      */
5039     getDataIndex : function(col){
5040         return this.config[col].dataIndex;
5041     },
5042
5043     /**
5044      * Sets the dataIndex for a column.
5045      * @param {Number} col The column index
5046      * @param {Number} dataIndex The new dataIndex
5047      */
5048     setDataIndex : function(col, dataIndex){
5049         this.config[col].dataIndex = dataIndex;
5050     },
5051
5052     
5053     
5054     /**
5055      * Returns true if the cell is editable.
5056      * @param {Number} colIndex The column index
5057      * @param {Number} rowIndex The row index
5058      * @return {Boolean}
5059      */
5060     isCellEditable : function(colIndex, rowIndex){
5061         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5062     },
5063
5064     /**
5065      * Returns the editor defined for the cell/column.
5066      * return false or null to disable editing.
5067      * @param {Number} colIndex The column index
5068      * @param {Number} rowIndex The row index
5069      * @return {Object}
5070      */
5071     getCellEditor : function(colIndex, rowIndex){
5072         return this.config[colIndex].editor;
5073     },
5074
5075     /**
5076      * Sets if a column is editable.
5077      * @param {Number} col The column index
5078      * @param {Boolean} editable True if the column is editable
5079      */
5080     setEditable : function(col, editable){
5081         this.config[col].editable = editable;
5082     },
5083
5084
5085     /**
5086      * Returns true if the column is hidden.
5087      * @param {Number} colIndex The column index
5088      * @return {Boolean}
5089      */
5090     isHidden : function(colIndex){
5091         return this.config[colIndex].hidden;
5092     },
5093
5094
5095     /**
5096      * Returns true if the column width cannot be changed
5097      */
5098     isFixed : function(colIndex){
5099         return this.config[colIndex].fixed;
5100     },
5101
5102     /**
5103      * Returns true if the column can be resized
5104      * @return {Boolean}
5105      */
5106     isResizable : function(colIndex){
5107         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5108     },
5109     /**
5110      * Sets if a column is hidden.
5111      * @param {Number} colIndex The column index
5112      * @param {Boolean} hidden True if the column is hidden
5113      */
5114     setHidden : function(colIndex, hidden){
5115         this.config[colIndex].hidden = hidden;
5116         this.totalWidth = null;
5117         this.fireEvent("hiddenchange", this, colIndex, hidden);
5118     },
5119
5120     /**
5121      * Sets the editor for a column.
5122      * @param {Number} col The column index
5123      * @param {Object} editor The editor object
5124      */
5125     setEditor : function(col, editor){
5126         this.config[col].editor = editor;
5127     }
5128 });
5129
5130 Roo.grid.ColumnModel.defaultRenderer = function(value){
5131         if(typeof value == "string" && value.length < 1){
5132             return "&#160;";
5133         }
5134         return value;
5135 };
5136
5137 // Alias for backwards compatibility
5138 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5139 /*
5140  * Based on:
5141  * Ext JS Library 1.1.1
5142  * Copyright(c) 2006-2007, Ext JS, LLC.
5143  *
5144  * Originally Released Under LGPL - original licence link has changed is not relivant.
5145  *
5146  * Fork - LGPL
5147  * <script type="text/javascript">
5148  */
5149  
5150 /**
5151  * @class Roo.LoadMask
5152  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5153  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5154  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5155  * element's UpdateManager load indicator and will be destroyed after the initial load.
5156  * @constructor
5157  * Create a new LoadMask
5158  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5159  * @param {Object} config The config object
5160  */
5161 Roo.LoadMask = function(el, config){
5162     this.el = Roo.get(el);
5163     Roo.apply(this, config);
5164     if(this.store){
5165         this.store.on('beforeload', this.onBeforeLoad, this);
5166         this.store.on('load', this.onLoad, this);
5167         this.store.on('loadexception', this.onLoadException, this);
5168         this.removeMask = false;
5169     }else{
5170         var um = this.el.getUpdateManager();
5171         um.showLoadIndicator = false; // disable the default indicator
5172         um.on('beforeupdate', this.onBeforeLoad, this);
5173         um.on('update', this.onLoad, this);
5174         um.on('failure', this.onLoad, this);
5175         this.removeMask = true;
5176     }
5177 };
5178
5179 Roo.LoadMask.prototype = {
5180     /**
5181      * @cfg {Boolean} removeMask
5182      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5183      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5184      */
5185     /**
5186      * @cfg {String} msg
5187      * The text to display in a centered loading message box (defaults to 'Loading...')
5188      */
5189     msg : 'Loading...',
5190     /**
5191      * @cfg {String} msgCls
5192      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5193      */
5194     msgCls : 'x-mask-loading',
5195
5196     /**
5197      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5198      * @type Boolean
5199      */
5200     disabled: false,
5201
5202     /**
5203      * Disables the mask to prevent it from being displayed
5204      */
5205     disable : function(){
5206        this.disabled = true;
5207     },
5208
5209     /**
5210      * Enables the mask so that it can be displayed
5211      */
5212     enable : function(){
5213         this.disabled = false;
5214     },
5215     
5216     onLoadException : function()
5217     {
5218         Roo.log(arguments);
5219         
5220         if (typeof(arguments[3]) != 'undefined') {
5221             Roo.MessageBox.alert("Error loading",arguments[3]);
5222         } 
5223         /*
5224         try {
5225             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5226                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5227             }   
5228         } catch(e) {
5229             
5230         }
5231         */
5232     
5233         
5234         
5235         this.el.unmask(this.removeMask);
5236     },
5237     // private
5238     onLoad : function()
5239     {
5240         this.el.unmask(this.removeMask);
5241     },
5242
5243     // private
5244     onBeforeLoad : function(){
5245         if(!this.disabled){
5246             this.el.mask(this.msg, this.msgCls);
5247         }
5248     },
5249
5250     // private
5251     destroy : function(){
5252         if(this.store){
5253             this.store.un('beforeload', this.onBeforeLoad, this);
5254             this.store.un('load', this.onLoad, this);
5255             this.store.un('loadexception', this.onLoadException, this);
5256         }else{
5257             var um = this.el.getUpdateManager();
5258             um.un('beforeupdate', this.onBeforeLoad, this);
5259             um.un('update', this.onLoad, this);
5260             um.un('failure', this.onLoad, this);
5261         }
5262     }
5263 };/*
5264  * - LGPL
5265  *
5266  * table
5267  * 
5268  */
5269
5270 /**
5271  * @class Roo.bootstrap.Table
5272  * @extends Roo.bootstrap.Component
5273  * Bootstrap Table class
5274  * @cfg {String} cls table class
5275  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5276  * @cfg {String} bgcolor Specifies the background color for a table
5277  * @cfg {Number} border Specifies whether the table cells should have borders or not
5278  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5279  * @cfg {Number} cellspacing Specifies the space between cells
5280  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5281  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5282  * @cfg {String} sortable Specifies that the table should be sortable
5283  * @cfg {String} summary Specifies a summary of the content of a table
5284  * @cfg {Number} width Specifies the width of a table
5285  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5286  * 
5287  * @cfg {boolean} striped Should the rows be alternative striped
5288  * @cfg {boolean} bordered Add borders to the table
5289  * @cfg {boolean} hover Add hover highlighting
5290  * @cfg {boolean} condensed Format condensed
5291  * @cfg {boolean} responsive Format condensed
5292  * @cfg {Boolean} loadMask (true|false) default false
5293  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5294  * @cfg {Boolean} thead (true|false) generate thead, default true
5295  * @cfg {Boolean} RowSelection (true|false) default false
5296  * @cfg {Boolean} CellSelection (true|false) default false
5297  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5298  
5299  * 
5300  * @constructor
5301  * Create a new Table
5302  * @param {Object} config The config object
5303  */
5304
5305 Roo.bootstrap.Table = function(config){
5306     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5307     
5308     if (this.sm) {
5309         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5310         this.sm = this.selModel;
5311         this.sm.xmodule = this.xmodule || false;
5312     }
5313     if (this.cm && typeof(this.cm.config) == 'undefined') {
5314         this.colModel = new Roo.grid.ColumnModel(this.cm);
5315         this.cm = this.colModel;
5316         this.cm.xmodule = this.xmodule || false;
5317     }
5318     if (this.store) {
5319         this.store= Roo.factory(this.store, Roo.data);
5320         this.ds = this.store;
5321         this.ds.xmodule = this.xmodule || false;
5322          
5323     }
5324     if (this.footer && this.store) {
5325         this.footer.dataSource = this.ds;
5326         this.footer = Roo.factory(this.footer);
5327     }
5328     
5329     /** @private */
5330     this.addEvents({
5331         /**
5332          * @event cellclick
5333          * Fires when a cell is clicked
5334          * @param {Roo.bootstrap.Table} this
5335          * @param {Roo.Element} el
5336          * @param {Number} rowIndex
5337          * @param {Number} columnIndex
5338          * @param {Roo.EventObject} e
5339          */
5340         "cellclick" : true,
5341         /**
5342          * @event celldblclick
5343          * Fires when a cell is double clicked
5344          * @param {Roo.bootstrap.Table} this
5345          * @param {Roo.Element} el
5346          * @param {Number} rowIndex
5347          * @param {Number} columnIndex
5348          * @param {Roo.EventObject} e
5349          */
5350         "celldblclick" : true,
5351         /**
5352          * @event rowclick
5353          * Fires when a row is clicked
5354          * @param {Roo.bootstrap.Table} this
5355          * @param {Roo.Element} el
5356          * @param {Number} rowIndex
5357          * @param {Roo.EventObject} e
5358          */
5359         "rowclick" : true,
5360         /**
5361          * @event rowdblclick
5362          * Fires when a row is double clicked
5363          * @param {Roo.bootstrap.Table} this
5364          * @param {Roo.Element} el
5365          * @param {Number} rowIndex
5366          * @param {Roo.EventObject} e
5367          */
5368         "rowdblclick" : true,
5369         /**
5370          * @event mouseover
5371          * Fires when a mouseover occur
5372          * @param {Roo.bootstrap.Table} this
5373          * @param {Roo.Element} el
5374          * @param {Number} rowIndex
5375          * @param {Number} columnIndex
5376          * @param {Roo.EventObject} e
5377          */
5378         "mouseover" : true,
5379         /**
5380          * @event mouseout
5381          * Fires when a mouseout occur
5382          * @param {Roo.bootstrap.Table} this
5383          * @param {Roo.Element} el
5384          * @param {Number} rowIndex
5385          * @param {Number} columnIndex
5386          * @param {Roo.EventObject} e
5387          */
5388         "mouseout" : true,
5389         /**
5390          * @event rowclass
5391          * Fires when a row is rendered, so you can change add a style to it.
5392          * @param {Roo.bootstrap.Table} this
5393          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5394          */
5395         'rowclass' : true,
5396           /**
5397          * @event rowsrendered
5398          * Fires when all the  rows have been rendered
5399          * @param {Roo.bootstrap.Table} this
5400          */
5401         'rowsrendered' : true
5402         
5403     });
5404 };
5405
5406 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5407     
5408     cls: false,
5409     align: false,
5410     bgcolor: false,
5411     border: false,
5412     cellpadding: false,
5413     cellspacing: false,
5414     frame: false,
5415     rules: false,
5416     sortable: false,
5417     summary: false,
5418     width: false,
5419     striped : false,
5420     bordered: false,
5421     hover:  false,
5422     condensed : false,
5423     responsive : false,
5424     sm : false,
5425     cm : false,
5426     store : false,
5427     loadMask : false,
5428     tfoot : true,
5429     thead : true,
5430     RowSelection : false,
5431     CellSelection : false,
5432     layout : false,
5433     
5434     // Roo.Element - the tbody
5435     mainBody: false, 
5436     
5437     getAutoCreate : function(){
5438         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5439         
5440         cfg = {
5441             tag: 'table',
5442             cls : 'table',
5443             cn : []
5444         }
5445             
5446         if (this.striped) {
5447             cfg.cls += ' table-striped';
5448         }
5449         
5450         if (this.hover) {
5451             cfg.cls += ' table-hover';
5452         }
5453         if (this.bordered) {
5454             cfg.cls += ' table-bordered';
5455         }
5456         if (this.condensed) {
5457             cfg.cls += ' table-condensed';
5458         }
5459         if (this.responsive) {
5460             cfg.cls += ' table-responsive';
5461         }
5462         
5463         if (this.cls) {
5464             cfg.cls+=  ' ' +this.cls;
5465         }
5466         
5467         // this lot should be simplifed...
5468         
5469         if (this.align) {
5470             cfg.align=this.align;
5471         }
5472         if (this.bgcolor) {
5473             cfg.bgcolor=this.bgcolor;
5474         }
5475         if (this.border) {
5476             cfg.border=this.border;
5477         }
5478         if (this.cellpadding) {
5479             cfg.cellpadding=this.cellpadding;
5480         }
5481         if (this.cellspacing) {
5482             cfg.cellspacing=this.cellspacing;
5483         }
5484         if (this.frame) {
5485             cfg.frame=this.frame;
5486         }
5487         if (this.rules) {
5488             cfg.rules=this.rules;
5489         }
5490         if (this.sortable) {
5491             cfg.sortable=this.sortable;
5492         }
5493         if (this.summary) {
5494             cfg.summary=this.summary;
5495         }
5496         if (this.width) {
5497             cfg.width=this.width;
5498         }
5499         if (this.layout) {
5500             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5501         }
5502         
5503         if(this.store || this.cm){
5504             if(this.thead){
5505                 cfg.cn.push(this.renderHeader());
5506             }
5507             
5508             cfg.cn.push(this.renderBody());
5509             
5510             if(this.tfoot){
5511                 cfg.cn.push(this.renderFooter());
5512             }
5513             
5514             cfg.cls+=  ' TableGrid';
5515         }
5516         
5517         return { cn : [ cfg ] };
5518     },
5519     
5520     initEvents : function()
5521     {   
5522         if(!this.store || !this.cm){
5523             return;
5524         }
5525         
5526         //Roo.log('initEvents with ds!!!!');
5527         
5528         this.mainBody = this.el.select('tbody', true).first();
5529         
5530         
5531         var _this = this;
5532         
5533         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5534             e.on('click', _this.sort, _this);
5535         });
5536         
5537         this.el.on("click", this.onClick, this);
5538         this.el.on("dblclick", this.onDblClick, this);
5539         
5540         // why is this done????? = it breaks dialogs??
5541         //this.parent().el.setStyle('position', 'relative');
5542         
5543         
5544         if (this.footer) {
5545             this.footer.parentId = this.id;
5546             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5547         }
5548         
5549         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5550         
5551         this.store.on('load', this.onLoad, this);
5552         this.store.on('beforeload', this.onBeforeLoad, this);
5553         this.store.on('update', this.onUpdate, this);
5554         this.store.on('add', this.onAdd, this);
5555         
5556     },
5557     
5558     onMouseover : function(e, el)
5559     {
5560         var cell = Roo.get(el);
5561         
5562         if(!cell){
5563             return;
5564         }
5565         
5566         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5567             cell = cell.findParent('td', false, true);
5568         }
5569         
5570         var row = cell.findParent('tr', false, true);
5571         var cellIndex = cell.dom.cellIndex;
5572         var rowIndex = row.dom.rowIndex - 1; // start from 0
5573         
5574         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5575         
5576     },
5577     
5578     onMouseout : function(e, el)
5579     {
5580         var cell = Roo.get(el);
5581         
5582         if(!cell){
5583             return;
5584         }
5585         
5586         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5587             cell = cell.findParent('td', false, true);
5588         }
5589         
5590         var row = cell.findParent('tr', false, true);
5591         var cellIndex = cell.dom.cellIndex;
5592         var rowIndex = row.dom.rowIndex - 1; // start from 0
5593         
5594         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5595         
5596     },
5597     
5598     onClick : function(e, el)
5599     {
5600         var cell = Roo.get(el);
5601         
5602         if(!cell || (!this.CellSelection && !this.RowSelection)){
5603             return;
5604         }
5605         
5606         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5607             cell = cell.findParent('td', false, true);
5608         }
5609         
5610         if(!cell || typeof(cell) == 'undefined'){
5611             return;
5612         }
5613         
5614         var row = cell.findParent('tr', false, true);
5615         
5616         if(!row || typeof(row) == 'undefined'){
5617             return;
5618         }
5619         
5620         var cellIndex = cell.dom.cellIndex;
5621         var rowIndex = this.getRowIndex(row);
5622         
5623         if(this.CellSelection){
5624             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5625         }
5626         
5627         if(this.RowSelection){
5628             this.fireEvent('rowclick', this, row, rowIndex, e);
5629         }
5630         
5631         
5632     },
5633     
5634     onDblClick : function(e,el)
5635     {
5636         var cell = Roo.get(el);
5637         
5638         if(!cell || (!this.CellSelection && !this.RowSelection)){
5639             return;
5640         }
5641         
5642         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5643             cell = cell.findParent('td', false, true);
5644         }
5645         
5646         if(!cell || typeof(cell) == 'undefined'){
5647             return;
5648         }
5649         
5650         var row = cell.findParent('tr', false, true);
5651         
5652         if(!row || typeof(row) == 'undefined'){
5653             return;
5654         }
5655         
5656         var cellIndex = cell.dom.cellIndex;
5657         var rowIndex = this.getRowIndex(row);
5658         
5659         if(this.CellSelection){
5660             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5661         }
5662         
5663         if(this.RowSelection){
5664             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5665         }
5666     },
5667     
5668     sort : function(e,el)
5669     {
5670         var col = Roo.get(el);
5671         
5672         if(!col.hasClass('sortable')){
5673             return;
5674         }
5675         
5676         var sort = col.attr('sort');
5677         var dir = 'ASC';
5678         
5679         if(col.hasClass('glyphicon-arrow-up')){
5680             dir = 'DESC';
5681         }
5682         
5683         this.store.sortInfo = {field : sort, direction : dir};
5684         
5685         if (this.footer) {
5686             Roo.log("calling footer first");
5687             this.footer.onClick('first');
5688         } else {
5689         
5690             this.store.load({ params : { start : 0 } });
5691         }
5692     },
5693     
5694     renderHeader : function()
5695     {
5696         var header = {
5697             tag: 'thead',
5698             cn : []
5699         };
5700         
5701         var cm = this.cm;
5702         
5703         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5704             
5705             var config = cm.config[i];
5706                     
5707             var c = {
5708                 tag: 'th',
5709                 style : '',
5710                 html: cm.getColumnHeader(i)
5711             };
5712             
5713             if(typeof(config.tooltip) != 'undefined'){
5714                 c.tooltip = config.tooltip;
5715             }
5716             
5717             if(typeof(config.colspan) != 'undefined'){
5718                 c.colspan = config.colspan;
5719             }
5720             
5721             if(typeof(config.hidden) != 'undefined' && config.hidden){
5722                 c.style += ' display:none;';
5723             }
5724             
5725             if(typeof(config.dataIndex) != 'undefined'){
5726                 c.sort = config.dataIndex;
5727             }
5728             
5729             if(typeof(config.sortable) != 'undefined' && config.sortable){
5730                 c.cls = 'sortable';
5731             }
5732             
5733             if(typeof(config.align) != 'undefined' && config.align.length){
5734                 c.style += ' text-align:' + config.align + ';';
5735             }
5736             
5737             if(typeof(config.width) != 'undefined'){
5738                 c.style += ' width:' + config.width + 'px;';
5739             }
5740             
5741             if(typeof(config.cls) != 'undefined'){
5742                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5743             }
5744             
5745             header.cn.push(c)
5746         }
5747         
5748         return header;
5749     },
5750     
5751     renderBody : function()
5752     {
5753         var body = {
5754             tag: 'tbody',
5755             cn : [
5756                 {
5757                     tag: 'tr',
5758                     cn : [
5759                         {
5760                             tag : 'td',
5761                             colspan :  this.cm.getColumnCount()
5762                         }
5763                     ]
5764                 }
5765             ]
5766         };
5767         
5768         return body;
5769     },
5770     
5771     renderFooter : function()
5772     {
5773         var footer = {
5774             tag: 'tfoot',
5775             cn : [
5776                 {
5777                     tag: 'tr',
5778                     cn : [
5779                         {
5780                             tag : 'td',
5781                             colspan :  this.cm.getColumnCount()
5782                         }
5783                     ]
5784                 }
5785             ]
5786         };
5787         
5788         return footer;
5789     },
5790     
5791     
5792     
5793     onLoad : function()
5794     {
5795         Roo.log('ds onload');
5796         this.clear();
5797         
5798         var _this = this;
5799         var cm = this.cm;
5800         var ds = this.store;
5801         
5802         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5803             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5804             
5805             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5806                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5807             }
5808             
5809             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5810                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5811             }
5812         });
5813         
5814         var tbody =  this.mainBody;
5815               
5816         if(ds.getCount() > 0){
5817             ds.data.each(function(d,rowIndex){
5818                 var row =  this.renderRow(cm, ds, rowIndex);
5819                 
5820                 tbody.createChild(row);
5821                 
5822                 var _this = this;
5823                 
5824                 if(row.cellObjects.length){
5825                     Roo.each(row.cellObjects, function(r){
5826                         _this.renderCellObject(r);
5827                     })
5828                 }
5829                 
5830             }, this);
5831         }
5832         
5833         Roo.each(this.el.select('tbody td', true).elements, function(e){
5834             e.on('mouseover', _this.onMouseover, _this);
5835         });
5836         
5837         Roo.each(this.el.select('tbody td', true).elements, function(e){
5838             e.on('mouseout', _this.onMouseout, _this);
5839         });
5840         this.fireEvent('rowsrendered', this);
5841         //if(this.loadMask){
5842         //    this.maskEl.hide();
5843         //}
5844     },
5845     
5846     
5847     onUpdate : function(ds,record)
5848     {
5849         this.refreshRow(record);
5850     },
5851     
5852     onRemove : function(ds, record, index, isUpdate){
5853         if(isUpdate !== true){
5854             this.fireEvent("beforerowremoved", this, index, record);
5855         }
5856         var bt = this.mainBody.dom;
5857         
5858         var rows = this.el.select('tbody > tr', true).elements;
5859         
5860         if(typeof(rows[index]) != 'undefined'){
5861             bt.removeChild(rows[index].dom);
5862         }
5863         
5864 //        if(bt.rows[index]){
5865 //            bt.removeChild(bt.rows[index]);
5866 //        }
5867         
5868         if(isUpdate !== true){
5869             //this.stripeRows(index);
5870             //this.syncRowHeights(index, index);
5871             //this.layout();
5872             this.fireEvent("rowremoved", this, index, record);
5873         }
5874     },
5875     
5876     onAdd : function(ds, records, rowIndex)
5877     {
5878         //Roo.log('on Add called');
5879         // - note this does not handle multiple adding very well..
5880         var bt = this.mainBody.dom;
5881         for (var i =0 ; i < records.length;i++) {
5882             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5883             //Roo.log(records[i]);
5884             //Roo.log(this.store.getAt(rowIndex+i));
5885             this.insertRow(this.store, rowIndex + i, false);
5886             return;
5887         }
5888         
5889     },
5890     
5891     
5892     refreshRow : function(record){
5893         var ds = this.store, index;
5894         if(typeof record == 'number'){
5895             index = record;
5896             record = ds.getAt(index);
5897         }else{
5898             index = ds.indexOf(record);
5899         }
5900         this.insertRow(ds, index, true);
5901         this.onRemove(ds, record, index+1, true);
5902         //this.syncRowHeights(index, index);
5903         //this.layout();
5904         this.fireEvent("rowupdated", this, index, record);
5905     },
5906     
5907     insertRow : function(dm, rowIndex, isUpdate){
5908         
5909         if(!isUpdate){
5910             this.fireEvent("beforerowsinserted", this, rowIndex);
5911         }
5912             //var s = this.getScrollState();
5913         var row = this.renderRow(this.cm, this.store, rowIndex);
5914         // insert before rowIndex..
5915         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5916         
5917         var _this = this;
5918                 
5919         if(row.cellObjects.length){
5920             Roo.each(row.cellObjects, function(r){
5921                 _this.renderCellObject(r);
5922             })
5923         }
5924             
5925         if(!isUpdate){
5926             this.fireEvent("rowsinserted", this, rowIndex);
5927             //this.syncRowHeights(firstRow, lastRow);
5928             //this.stripeRows(firstRow);
5929             //this.layout();
5930         }
5931         
5932     },
5933     
5934     
5935     getRowDom : function(rowIndex)
5936     {
5937         var rows = this.el.select('tbody > tr', true).elements;
5938         
5939         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5940         
5941     },
5942     // returns the object tree for a tr..
5943   
5944     
5945     renderRow : function(cm, ds, rowIndex) 
5946     {
5947         
5948         var d = ds.getAt(rowIndex);
5949         
5950         var row = {
5951             tag : 'tr',
5952             cn : []
5953         };
5954             
5955         var cellObjects = [];
5956         
5957         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5958             var config = cm.config[i];
5959             
5960             var renderer = cm.getRenderer(i);
5961             var value = '';
5962             var id = false;
5963             
5964             if(typeof(renderer) !== 'undefined'){
5965                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5966             }
5967             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5968             // and are rendered into the cells after the row is rendered - using the id for the element.
5969             
5970             if(typeof(value) === 'object'){
5971                 id = Roo.id();
5972                 cellObjects.push({
5973                     container : id,
5974                     cfg : value 
5975                 })
5976             }
5977             
5978             var rowcfg = {
5979                 record: d,
5980                 rowIndex : rowIndex,
5981                 colIndex : i,
5982                 rowClass : ''
5983             }
5984
5985             this.fireEvent('rowclass', this, rowcfg);
5986             
5987             var td = {
5988                 tag: 'td',
5989                 cls : rowcfg.rowClass,
5990                 style: '',
5991                 html: (typeof(value) === 'object') ? '' : value
5992             };
5993             
5994             if (id) {
5995                 td.id = id;
5996             }
5997             
5998             if(typeof(config.colspan) != 'undefined'){
5999                 td.colspan = config.colspan;
6000             }
6001             
6002             if(typeof(config.hidden) != 'undefined' && config.hidden){
6003                 td.style += ' display:none;';
6004             }
6005             
6006             if(typeof(config.align) != 'undefined' && config.align.length){
6007                 td.style += ' text-align:' + config.align + ';';
6008             }
6009             
6010             if(typeof(config.width) != 'undefined'){
6011                 td.style += ' width:' +  config.width + 'px;';
6012             }
6013             
6014             if(typeof(config.cursor) != 'undefined'){
6015                 td.style += ' cursor:' +  config.cursor + ';';
6016             }
6017             
6018             if(typeof(config.cls) != 'undefined'){
6019                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6020             }
6021              
6022             row.cn.push(td);
6023            
6024         }
6025         
6026         row.cellObjects = cellObjects;
6027         
6028         return row;
6029           
6030     },
6031     
6032     
6033     
6034     onBeforeLoad : function()
6035     {
6036         //Roo.log('ds onBeforeLoad');
6037         
6038         //this.clear();
6039         
6040         //if(this.loadMask){
6041         //    this.maskEl.show();
6042         //}
6043     },
6044      /**
6045      * Remove all rows
6046      */
6047     clear : function()
6048     {
6049         this.el.select('tbody', true).first().dom.innerHTML = '';
6050     },
6051     /**
6052      * Show or hide a row.
6053      * @param {Number} rowIndex to show or hide
6054      * @param {Boolean} state hide
6055      */
6056     setRowVisibility : function(rowIndex, state)
6057     {
6058         var bt = this.mainBody.dom;
6059         
6060         var rows = this.el.select('tbody > tr', true).elements;
6061         
6062         if(typeof(rows[rowIndex]) == 'undefined'){
6063             return;
6064         }
6065         rows[rowIndex].dom.style.display = state ? '' : 'none';
6066     },
6067     
6068     
6069     getSelectionModel : function(){
6070         if(!this.selModel){
6071             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6072         }
6073         return this.selModel;
6074     },
6075     /*
6076      * Render the Roo.bootstrap object from renderder
6077      */
6078     renderCellObject : function(r)
6079     {
6080         var _this = this;
6081         
6082         var t = r.cfg.render(r.container);
6083         
6084         if(r.cfg.cn){
6085             Roo.each(r.cfg.cn, function(c){
6086                 var child = {
6087                     container: t.getChildContainer(),
6088                     cfg: c
6089                 }
6090                 _this.renderCellObject(child);
6091             })
6092         }
6093     },
6094     
6095     getRowIndex : function(row)
6096     {
6097         var rowIndex = -1;
6098         
6099         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6100             if(el != row){
6101                 return;
6102             }
6103             
6104             rowIndex = index;
6105         });
6106         
6107         return rowIndex;
6108     }
6109    
6110 });
6111
6112  
6113
6114  /*
6115  * - LGPL
6116  *
6117  * table cell
6118  * 
6119  */
6120
6121 /**
6122  * @class Roo.bootstrap.TableCell
6123  * @extends Roo.bootstrap.Component
6124  * Bootstrap TableCell class
6125  * @cfg {String} html cell contain text
6126  * @cfg {String} cls cell class
6127  * @cfg {String} tag cell tag (td|th) default td
6128  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6129  * @cfg {String} align Aligns the content in a cell
6130  * @cfg {String} axis Categorizes cells
6131  * @cfg {String} bgcolor Specifies the background color of a cell
6132  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6133  * @cfg {Number} colspan Specifies the number of columns a cell should span
6134  * @cfg {String} headers Specifies one or more header cells a cell is related to
6135  * @cfg {Number} height Sets the height of a cell
6136  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6137  * @cfg {Number} rowspan Sets the number of rows a cell should span
6138  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6139  * @cfg {String} valign Vertical aligns the content in a cell
6140  * @cfg {Number} width Specifies the width of a cell
6141  * 
6142  * @constructor
6143  * Create a new TableCell
6144  * @param {Object} config The config object
6145  */
6146
6147 Roo.bootstrap.TableCell = function(config){
6148     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6149 };
6150
6151 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6152     
6153     html: false,
6154     cls: false,
6155     tag: false,
6156     abbr: false,
6157     align: false,
6158     axis: false,
6159     bgcolor: false,
6160     charoff: false,
6161     colspan: false,
6162     headers: false,
6163     height: false,
6164     nowrap: false,
6165     rowspan: false,
6166     scope: false,
6167     valign: false,
6168     width: false,
6169     
6170     
6171     getAutoCreate : function(){
6172         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6173         
6174         cfg = {
6175             tag: 'td'
6176         }
6177         
6178         if(this.tag){
6179             cfg.tag = this.tag;
6180         }
6181         
6182         if (this.html) {
6183             cfg.html=this.html
6184         }
6185         if (this.cls) {
6186             cfg.cls=this.cls
6187         }
6188         if (this.abbr) {
6189             cfg.abbr=this.abbr
6190         }
6191         if (this.align) {
6192             cfg.align=this.align
6193         }
6194         if (this.axis) {
6195             cfg.axis=this.axis
6196         }
6197         if (this.bgcolor) {
6198             cfg.bgcolor=this.bgcolor
6199         }
6200         if (this.charoff) {
6201             cfg.charoff=this.charoff
6202         }
6203         if (this.colspan) {
6204             cfg.colspan=this.colspan
6205         }
6206         if (this.headers) {
6207             cfg.headers=this.headers
6208         }
6209         if (this.height) {
6210             cfg.height=this.height
6211         }
6212         if (this.nowrap) {
6213             cfg.nowrap=this.nowrap
6214         }
6215         if (this.rowspan) {
6216             cfg.rowspan=this.rowspan
6217         }
6218         if (this.scope) {
6219             cfg.scope=this.scope
6220         }
6221         if (this.valign) {
6222             cfg.valign=this.valign
6223         }
6224         if (this.width) {
6225             cfg.width=this.width
6226         }
6227         
6228         
6229         return cfg;
6230     }
6231    
6232 });
6233
6234  
6235
6236  /*
6237  * - LGPL
6238  *
6239  * table row
6240  * 
6241  */
6242
6243 /**
6244  * @class Roo.bootstrap.TableRow
6245  * @extends Roo.bootstrap.Component
6246  * Bootstrap TableRow class
6247  * @cfg {String} cls row class
6248  * @cfg {String} align Aligns the content in a table row
6249  * @cfg {String} bgcolor Specifies a background color for a table row
6250  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6251  * @cfg {String} valign Vertical aligns the content in a table row
6252  * 
6253  * @constructor
6254  * Create a new TableRow
6255  * @param {Object} config The config object
6256  */
6257
6258 Roo.bootstrap.TableRow = function(config){
6259     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6260 };
6261
6262 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6263     
6264     cls: false,
6265     align: false,
6266     bgcolor: false,
6267     charoff: false,
6268     valign: false,
6269     
6270     getAutoCreate : function(){
6271         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6272         
6273         cfg = {
6274             tag: 'tr'
6275         }
6276             
6277         if(this.cls){
6278             cfg.cls = this.cls;
6279         }
6280         if(this.align){
6281             cfg.align = this.align;
6282         }
6283         if(this.bgcolor){
6284             cfg.bgcolor = this.bgcolor;
6285         }
6286         if(this.charoff){
6287             cfg.charoff = this.charoff;
6288         }
6289         if(this.valign){
6290             cfg.valign = this.valign;
6291         }
6292         
6293         return cfg;
6294     }
6295    
6296 });
6297
6298  
6299
6300  /*
6301  * - LGPL
6302  *
6303  * table body
6304  * 
6305  */
6306
6307 /**
6308  * @class Roo.bootstrap.TableBody
6309  * @extends Roo.bootstrap.Component
6310  * Bootstrap TableBody class
6311  * @cfg {String} cls element class
6312  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6313  * @cfg {String} align Aligns the content inside the element
6314  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6315  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6316  * 
6317  * @constructor
6318  * Create a new TableBody
6319  * @param {Object} config The config object
6320  */
6321
6322 Roo.bootstrap.TableBody = function(config){
6323     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6324 };
6325
6326 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6327     
6328     cls: false,
6329     tag: false,
6330     align: false,
6331     charoff: false,
6332     valign: false,
6333     
6334     getAutoCreate : function(){
6335         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6336         
6337         cfg = {
6338             tag: 'tbody'
6339         }
6340             
6341         if (this.cls) {
6342             cfg.cls=this.cls
6343         }
6344         if(this.tag){
6345             cfg.tag = this.tag;
6346         }
6347         
6348         if(this.align){
6349             cfg.align = this.align;
6350         }
6351         if(this.charoff){
6352             cfg.charoff = this.charoff;
6353         }
6354         if(this.valign){
6355             cfg.valign = this.valign;
6356         }
6357         
6358         return cfg;
6359     }
6360     
6361     
6362 //    initEvents : function()
6363 //    {
6364 //        
6365 //        if(!this.store){
6366 //            return;
6367 //        }
6368 //        
6369 //        this.store = Roo.factory(this.store, Roo.data);
6370 //        this.store.on('load', this.onLoad, this);
6371 //        
6372 //        this.store.load();
6373 //        
6374 //    },
6375 //    
6376 //    onLoad: function () 
6377 //    {   
6378 //        this.fireEvent('load', this);
6379 //    }
6380 //    
6381 //   
6382 });
6383
6384  
6385
6386  /*
6387  * Based on:
6388  * Ext JS Library 1.1.1
6389  * Copyright(c) 2006-2007, Ext JS, LLC.
6390  *
6391  * Originally Released Under LGPL - original licence link has changed is not relivant.
6392  *
6393  * Fork - LGPL
6394  * <script type="text/javascript">
6395  */
6396
6397 // as we use this in bootstrap.
6398 Roo.namespace('Roo.form');
6399  /**
6400  * @class Roo.form.Action
6401  * Internal Class used to handle form actions
6402  * @constructor
6403  * @param {Roo.form.BasicForm} el The form element or its id
6404  * @param {Object} config Configuration options
6405  */
6406
6407  
6408  
6409 // define the action interface
6410 Roo.form.Action = function(form, options){
6411     this.form = form;
6412     this.options = options || {};
6413 };
6414 /**
6415  * Client Validation Failed
6416  * @const 
6417  */
6418 Roo.form.Action.CLIENT_INVALID = 'client';
6419 /**
6420  * Server Validation Failed
6421  * @const 
6422  */
6423 Roo.form.Action.SERVER_INVALID = 'server';
6424  /**
6425  * Connect to Server Failed
6426  * @const 
6427  */
6428 Roo.form.Action.CONNECT_FAILURE = 'connect';
6429 /**
6430  * Reading Data from Server Failed
6431  * @const 
6432  */
6433 Roo.form.Action.LOAD_FAILURE = 'load';
6434
6435 Roo.form.Action.prototype = {
6436     type : 'default',
6437     failureType : undefined,
6438     response : undefined,
6439     result : undefined,
6440
6441     // interface method
6442     run : function(options){
6443
6444     },
6445
6446     // interface method
6447     success : function(response){
6448
6449     },
6450
6451     // interface method
6452     handleResponse : function(response){
6453
6454     },
6455
6456     // default connection failure
6457     failure : function(response){
6458         
6459         this.response = response;
6460         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6461         this.form.afterAction(this, false);
6462     },
6463
6464     processResponse : function(response){
6465         this.response = response;
6466         if(!response.responseText){
6467             return true;
6468         }
6469         this.result = this.handleResponse(response);
6470         return this.result;
6471     },
6472
6473     // utility functions used internally
6474     getUrl : function(appendParams){
6475         var url = this.options.url || this.form.url || this.form.el.dom.action;
6476         if(appendParams){
6477             var p = this.getParams();
6478             if(p){
6479                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6480             }
6481         }
6482         return url;
6483     },
6484
6485     getMethod : function(){
6486         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6487     },
6488
6489     getParams : function(){
6490         var bp = this.form.baseParams;
6491         var p = this.options.params;
6492         if(p){
6493             if(typeof p == "object"){
6494                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6495             }else if(typeof p == 'string' && bp){
6496                 p += '&' + Roo.urlEncode(bp);
6497             }
6498         }else if(bp){
6499             p = Roo.urlEncode(bp);
6500         }
6501         return p;
6502     },
6503
6504     createCallback : function(){
6505         return {
6506             success: this.success,
6507             failure: this.failure,
6508             scope: this,
6509             timeout: (this.form.timeout*1000),
6510             upload: this.form.fileUpload ? this.success : undefined
6511         };
6512     }
6513 };
6514
6515 Roo.form.Action.Submit = function(form, options){
6516     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6517 };
6518
6519 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6520     type : 'submit',
6521
6522     haveProgress : false,
6523     uploadComplete : false,
6524     
6525     // uploadProgress indicator.
6526     uploadProgress : function()
6527     {
6528         if (!this.form.progressUrl) {
6529             return;
6530         }
6531         
6532         if (!this.haveProgress) {
6533             Roo.MessageBox.progress("Uploading", "Uploading");
6534         }
6535         if (this.uploadComplete) {
6536            Roo.MessageBox.hide();
6537            return;
6538         }
6539         
6540         this.haveProgress = true;
6541    
6542         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6543         
6544         var c = new Roo.data.Connection();
6545         c.request({
6546             url : this.form.progressUrl,
6547             params: {
6548                 id : uid
6549             },
6550             method: 'GET',
6551             success : function(req){
6552                //console.log(data);
6553                 var rdata = false;
6554                 var edata;
6555                 try  {
6556                    rdata = Roo.decode(req.responseText)
6557                 } catch (e) {
6558                     Roo.log("Invalid data from server..");
6559                     Roo.log(edata);
6560                     return;
6561                 }
6562                 if (!rdata || !rdata.success) {
6563                     Roo.log(rdata);
6564                     Roo.MessageBox.alert(Roo.encode(rdata));
6565                     return;
6566                 }
6567                 var data = rdata.data;
6568                 
6569                 if (this.uploadComplete) {
6570                    Roo.MessageBox.hide();
6571                    return;
6572                 }
6573                    
6574                 if (data){
6575                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6576                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6577                     );
6578                 }
6579                 this.uploadProgress.defer(2000,this);
6580             },
6581        
6582             failure: function(data) {
6583                 Roo.log('progress url failed ');
6584                 Roo.log(data);
6585             },
6586             scope : this
6587         });
6588            
6589     },
6590     
6591     
6592     run : function()
6593     {
6594         // run get Values on the form, so it syncs any secondary forms.
6595         this.form.getValues();
6596         
6597         var o = this.options;
6598         var method = this.getMethod();
6599         var isPost = method == 'POST';
6600         if(o.clientValidation === false || this.form.isValid()){
6601             
6602             if (this.form.progressUrl) {
6603                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6604                     (new Date() * 1) + '' + Math.random());
6605                     
6606             } 
6607             
6608             
6609             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6610                 form:this.form.el.dom,
6611                 url:this.getUrl(!isPost),
6612                 method: method,
6613                 params:isPost ? this.getParams() : null,
6614                 isUpload: this.form.fileUpload
6615             }));
6616             
6617             this.uploadProgress();
6618
6619         }else if (o.clientValidation !== false){ // client validation failed
6620             this.failureType = Roo.form.Action.CLIENT_INVALID;
6621             this.form.afterAction(this, false);
6622         }
6623     },
6624
6625     success : function(response)
6626     {
6627         this.uploadComplete= true;
6628         if (this.haveProgress) {
6629             Roo.MessageBox.hide();
6630         }
6631         
6632         
6633         var result = this.processResponse(response);
6634         if(result === true || result.success){
6635             this.form.afterAction(this, true);
6636             return;
6637         }
6638         if(result.errors){
6639             this.form.markInvalid(result.errors);
6640             this.failureType = Roo.form.Action.SERVER_INVALID;
6641         }
6642         this.form.afterAction(this, false);
6643     },
6644     failure : function(response)
6645     {
6646         this.uploadComplete= true;
6647         if (this.haveProgress) {
6648             Roo.MessageBox.hide();
6649         }
6650         
6651         this.response = response;
6652         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6653         this.form.afterAction(this, false);
6654     },
6655     
6656     handleResponse : function(response){
6657         if(this.form.errorReader){
6658             var rs = this.form.errorReader.read(response);
6659             var errors = [];
6660             if(rs.records){
6661                 for(var i = 0, len = rs.records.length; i < len; i++) {
6662                     var r = rs.records[i];
6663                     errors[i] = r.data;
6664                 }
6665             }
6666             if(errors.length < 1){
6667                 errors = null;
6668             }
6669             return {
6670                 success : rs.success,
6671                 errors : errors
6672             };
6673         }
6674         var ret = false;
6675         try {
6676             ret = Roo.decode(response.responseText);
6677         } catch (e) {
6678             ret = {
6679                 success: false,
6680                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6681                 errors : []
6682             };
6683         }
6684         return ret;
6685         
6686     }
6687 });
6688
6689
6690 Roo.form.Action.Load = function(form, options){
6691     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6692     this.reader = this.form.reader;
6693 };
6694
6695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6696     type : 'load',
6697
6698     run : function(){
6699         
6700         Roo.Ajax.request(Roo.apply(
6701                 this.createCallback(), {
6702                     method:this.getMethod(),
6703                     url:this.getUrl(false),
6704                     params:this.getParams()
6705         }));
6706     },
6707
6708     success : function(response){
6709         
6710         var result = this.processResponse(response);
6711         if(result === true || !result.success || !result.data){
6712             this.failureType = Roo.form.Action.LOAD_FAILURE;
6713             this.form.afterAction(this, false);
6714             return;
6715         }
6716         this.form.clearInvalid();
6717         this.form.setValues(result.data);
6718         this.form.afterAction(this, true);
6719     },
6720
6721     handleResponse : function(response){
6722         if(this.form.reader){
6723             var rs = this.form.reader.read(response);
6724             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6725             return {
6726                 success : rs.success,
6727                 data : data
6728             };
6729         }
6730         return Roo.decode(response.responseText);
6731     }
6732 });
6733
6734 Roo.form.Action.ACTION_TYPES = {
6735     'load' : Roo.form.Action.Load,
6736     'submit' : Roo.form.Action.Submit
6737 };/*
6738  * - LGPL
6739  *
6740  * form
6741  * 
6742  */
6743
6744 /**
6745  * @class Roo.bootstrap.Form
6746  * @extends Roo.bootstrap.Component
6747  * Bootstrap Form class
6748  * @cfg {String} method  GET | POST (default POST)
6749  * @cfg {String} labelAlign top | left (default top)
6750  * @cfg {String} align left  | right - for navbars
6751  * @cfg {Boolean} loadMask load mask when submit (default true)
6752
6753  * 
6754  * @constructor
6755  * Create a new Form
6756  * @param {Object} config The config object
6757  */
6758
6759
6760 Roo.bootstrap.Form = function(config){
6761     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6762     this.addEvents({
6763         /**
6764          * @event clientvalidation
6765          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6766          * @param {Form} this
6767          * @param {Boolean} valid true if the form has passed client-side validation
6768          */
6769         clientvalidation: true,
6770         /**
6771          * @event beforeaction
6772          * Fires before any action is performed. Return false to cancel the action.
6773          * @param {Form} this
6774          * @param {Action} action The action to be performed
6775          */
6776         beforeaction: true,
6777         /**
6778          * @event actionfailed
6779          * Fires when an action fails.
6780          * @param {Form} this
6781          * @param {Action} action The action that failed
6782          */
6783         actionfailed : true,
6784         /**
6785          * @event actioncomplete
6786          * Fires when an action is completed.
6787          * @param {Form} this
6788          * @param {Action} action The action that completed
6789          */
6790         actioncomplete : true
6791     });
6792     
6793 };
6794
6795 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6796       
6797      /**
6798      * @cfg {String} method
6799      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6800      */
6801     method : 'POST',
6802     /**
6803      * @cfg {String} url
6804      * The URL to use for form actions if one isn't supplied in the action options.
6805      */
6806     /**
6807      * @cfg {Boolean} fileUpload
6808      * Set to true if this form is a file upload.
6809      */
6810      
6811     /**
6812      * @cfg {Object} baseParams
6813      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6814      */
6815       
6816     /**
6817      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6818      */
6819     timeout: 30,
6820     /**
6821      * @cfg {Sting} align (left|right) for navbar forms
6822      */
6823     align : 'left',
6824
6825     // private
6826     activeAction : null,
6827  
6828     /**
6829      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6830      * element by passing it or its id or mask the form itself by passing in true.
6831      * @type Mixed
6832      */
6833     waitMsgTarget : false,
6834     
6835     loadMask : true,
6836     
6837     getAutoCreate : function(){
6838         
6839         var cfg = {
6840             tag: 'form',
6841             method : this.method || 'POST',
6842             id : this.id || Roo.id(),
6843             cls : ''
6844         }
6845         if (this.parent().xtype.match(/^Nav/)) {
6846             cfg.cls = 'navbar-form navbar-' + this.align;
6847             
6848         }
6849         
6850         if (this.labelAlign == 'left' ) {
6851             cfg.cls += ' form-horizontal';
6852         }
6853         
6854         
6855         return cfg;
6856     },
6857     initEvents : function()
6858     {
6859         this.el.on('submit', this.onSubmit, this);
6860         // this was added as random key presses on the form where triggering form submit.
6861         this.el.on('keypress', function(e) {
6862             if (e.getCharCode() != 13) {
6863                 return true;
6864             }
6865             // we might need to allow it for textareas.. and some other items.
6866             // check e.getTarget().
6867             
6868             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6869                 return true;
6870             }
6871         
6872             Roo.log("keypress blocked");
6873             
6874             e.preventDefault();
6875             return false;
6876         });
6877         
6878     },
6879     // private
6880     onSubmit : function(e){
6881         e.stopEvent();
6882     },
6883     
6884      /**
6885      * Returns true if client-side validation on the form is successful.
6886      * @return Boolean
6887      */
6888     isValid : function(){
6889         var items = this.getItems();
6890         var valid = true;
6891         items.each(function(f){
6892            if(!f.validate()){
6893                valid = false;
6894                
6895            }
6896         });
6897         return valid;
6898     },
6899     /**
6900      * Returns true if any fields in this form have changed since their original load.
6901      * @return Boolean
6902      */
6903     isDirty : function(){
6904         var dirty = false;
6905         var items = this.getItems();
6906         items.each(function(f){
6907            if(f.isDirty()){
6908                dirty = true;
6909                return false;
6910            }
6911            return true;
6912         });
6913         return dirty;
6914     },
6915      /**
6916      * Performs a predefined action (submit or load) or custom actions you define on this form.
6917      * @param {String} actionName The name of the action type
6918      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6919      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6920      * accept other config options):
6921      * <pre>
6922 Property          Type             Description
6923 ----------------  ---------------  ----------------------------------------------------------------------------------
6924 url               String           The url for the action (defaults to the form's url)
6925 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6926 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6927 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6928                                    validate the form on the client (defaults to false)
6929      * </pre>
6930      * @return {BasicForm} this
6931      */
6932     doAction : function(action, options){
6933         if(typeof action == 'string'){
6934             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6935         }
6936         if(this.fireEvent('beforeaction', this, action) !== false){
6937             this.beforeAction(action);
6938             action.run.defer(100, action);
6939         }
6940         return this;
6941     },
6942     
6943     // private
6944     beforeAction : function(action){
6945         var o = action.options;
6946         
6947         if(this.loadMask){
6948             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6949         }
6950         // not really supported yet.. ??
6951         
6952         //if(this.waitMsgTarget === true){
6953         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6954         //}else if(this.waitMsgTarget){
6955         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6956         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6957         //}else {
6958         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6959        // }
6960          
6961     },
6962
6963     // private
6964     afterAction : function(action, success){
6965         this.activeAction = null;
6966         var o = action.options;
6967         
6968         //if(this.waitMsgTarget === true){
6969             this.el.unmask();
6970         //}else if(this.waitMsgTarget){
6971         //    this.waitMsgTarget.unmask();
6972         //}else{
6973         //    Roo.MessageBox.updateProgress(1);
6974         //    Roo.MessageBox.hide();
6975        // }
6976         // 
6977         if(success){
6978             if(o.reset){
6979                 this.reset();
6980             }
6981             Roo.callback(o.success, o.scope, [this, action]);
6982             this.fireEvent('actioncomplete', this, action);
6983             
6984         }else{
6985             
6986             // failure condition..
6987             // we have a scenario where updates need confirming.
6988             // eg. if a locking scenario exists..
6989             // we look for { errors : { needs_confirm : true }} in the response.
6990             if (
6991                 (typeof(action.result) != 'undefined')  &&
6992                 (typeof(action.result.errors) != 'undefined')  &&
6993                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6994            ){
6995                 var _t = this;
6996                 Roo.log("not supported yet");
6997                  /*
6998                 
6999                 Roo.MessageBox.confirm(
7000                     "Change requires confirmation",
7001                     action.result.errorMsg,
7002                     function(r) {
7003                         if (r != 'yes') {
7004                             return;
7005                         }
7006                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7007                     }
7008                     
7009                 );
7010                 */
7011                 
7012                 
7013                 return;
7014             }
7015             
7016             Roo.callback(o.failure, o.scope, [this, action]);
7017             // show an error message if no failed handler is set..
7018             if (!this.hasListener('actionfailed')) {
7019                 Roo.log("need to add dialog support");
7020                 /*
7021                 Roo.MessageBox.alert("Error",
7022                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7023                         action.result.errorMsg :
7024                         "Saving Failed, please check your entries or try again"
7025                 );
7026                 */
7027             }
7028             
7029             this.fireEvent('actionfailed', this, action);
7030         }
7031         
7032     },
7033     /**
7034      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7035      * @param {String} id The value to search for
7036      * @return Field
7037      */
7038     findField : function(id){
7039         var items = this.getItems();
7040         var field = items.get(id);
7041         if(!field){
7042              items.each(function(f){
7043                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7044                     field = f;
7045                     return false;
7046                 }
7047                 return true;
7048             });
7049         }
7050         return field || null;
7051     },
7052      /**
7053      * Mark fields in this form invalid in bulk.
7054      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7055      * @return {BasicForm} this
7056      */
7057     markInvalid : function(errors){
7058         if(errors instanceof Array){
7059             for(var i = 0, len = errors.length; i < len; i++){
7060                 var fieldError = errors[i];
7061                 var f = this.findField(fieldError.id);
7062                 if(f){
7063                     f.markInvalid(fieldError.msg);
7064                 }
7065             }
7066         }else{
7067             var field, id;
7068             for(id in errors){
7069                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7070                     field.markInvalid(errors[id]);
7071                 }
7072             }
7073         }
7074         //Roo.each(this.childForms || [], function (f) {
7075         //    f.markInvalid(errors);
7076         //});
7077         
7078         return this;
7079     },
7080
7081     /**
7082      * Set values for fields in this form in bulk.
7083      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7084      * @return {BasicForm} this
7085      */
7086     setValues : function(values){
7087         if(values instanceof Array){ // array of objects
7088             for(var i = 0, len = values.length; i < len; i++){
7089                 var v = values[i];
7090                 var f = this.findField(v.id);
7091                 if(f){
7092                     f.setValue(v.value);
7093                     if(this.trackResetOnLoad){
7094                         f.originalValue = f.getValue();
7095                     }
7096                 }
7097             }
7098         }else{ // object hash
7099             var field, id;
7100             for(id in values){
7101                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7102                     
7103                     if (field.setFromData && 
7104                         field.valueField && 
7105                         field.displayField &&
7106                         // combos' with local stores can 
7107                         // be queried via setValue()
7108                         // to set their value..
7109                         (field.store && !field.store.isLocal)
7110                         ) {
7111                         // it's a combo
7112                         var sd = { };
7113                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7114                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7115                         field.setFromData(sd);
7116                         
7117                     } else {
7118                         field.setValue(values[id]);
7119                     }
7120                     
7121                     
7122                     if(this.trackResetOnLoad){
7123                         field.originalValue = field.getValue();
7124                     }
7125                 }
7126             }
7127         }
7128          
7129         //Roo.each(this.childForms || [], function (f) {
7130         //    f.setValues(values);
7131         //});
7132                 
7133         return this;
7134     },
7135
7136     /**
7137      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7138      * they are returned as an array.
7139      * @param {Boolean} asString
7140      * @return {Object}
7141      */
7142     getValues : function(asString){
7143         //if (this.childForms) {
7144             // copy values from the child forms
7145         //    Roo.each(this.childForms, function (f) {
7146         //        this.setValues(f.getValues());
7147         //    }, this);
7148         //}
7149         
7150         
7151         
7152         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7153         if(asString === true){
7154             return fs;
7155         }
7156         return Roo.urlDecode(fs);
7157     },
7158     
7159     /**
7160      * Returns the fields in this form as an object with key/value pairs. 
7161      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7162      * @return {Object}
7163      */
7164     getFieldValues : function(with_hidden)
7165     {
7166         var items = this.getItems();
7167         var ret = {};
7168         items.each(function(f){
7169             if (!f.getName()) {
7170                 return;
7171             }
7172             var v = f.getValue();
7173             if (f.inputType =='radio') {
7174                 if (typeof(ret[f.getName()]) == 'undefined') {
7175                     ret[f.getName()] = ''; // empty..
7176                 }
7177                 
7178                 if (!f.el.dom.checked) {
7179                     return;
7180                     
7181                 }
7182                 v = f.el.dom.value;
7183                 
7184             }
7185             
7186             // not sure if this supported any more..
7187             if ((typeof(v) == 'object') && f.getRawValue) {
7188                 v = f.getRawValue() ; // dates..
7189             }
7190             // combo boxes where name != hiddenName...
7191             if (f.name != f.getName()) {
7192                 ret[f.name] = f.getRawValue();
7193             }
7194             ret[f.getName()] = v;
7195         });
7196         
7197         return ret;
7198     },
7199
7200     /**
7201      * Clears all invalid messages in this form.
7202      * @return {BasicForm} this
7203      */
7204     clearInvalid : function(){
7205         var items = this.getItems();
7206         
7207         items.each(function(f){
7208            f.clearInvalid();
7209         });
7210         
7211         
7212         
7213         return this;
7214     },
7215
7216     /**
7217      * Resets this form.
7218      * @return {BasicForm} this
7219      */
7220     reset : function(){
7221         var items = this.getItems();
7222         items.each(function(f){
7223             f.reset();
7224         });
7225         
7226         Roo.each(this.childForms || [], function (f) {
7227             f.reset();
7228         });
7229        
7230         
7231         return this;
7232     },
7233     getItems : function()
7234     {
7235         var r=new Roo.util.MixedCollection(false, function(o){
7236             return o.id || (o.id = Roo.id());
7237         });
7238         var iter = function(el) {
7239             if (el.inputEl) {
7240                 r.add(el);
7241             }
7242             if (!el.items) {
7243                 return;
7244             }
7245             Roo.each(el.items,function(e) {
7246                 iter(e);
7247             });
7248             
7249             
7250         };
7251         
7252         iter(this);
7253         return r;
7254         
7255         
7256         
7257         
7258     }
7259     
7260 });
7261
7262  
7263 /*
7264  * Based on:
7265  * Ext JS Library 1.1.1
7266  * Copyright(c) 2006-2007, Ext JS, LLC.
7267  *
7268  * Originally Released Under LGPL - original licence link has changed is not relivant.
7269  *
7270  * Fork - LGPL
7271  * <script type="text/javascript">
7272  */
7273 /**
7274  * @class Roo.form.VTypes
7275  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7276  * @singleton
7277  */
7278 Roo.form.VTypes = function(){
7279     // closure these in so they are only created once.
7280     var alpha = /^[a-zA-Z_]+$/;
7281     var alphanum = /^[a-zA-Z0-9_]+$/;
7282     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7283     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7284
7285     // All these messages and functions are configurable
7286     return {
7287         /**
7288          * The function used to validate email addresses
7289          * @param {String} value The email address
7290          */
7291         'email' : function(v){
7292             return email.test(v);
7293         },
7294         /**
7295          * The error text to display when the email validation function returns false
7296          * @type String
7297          */
7298         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7299         /**
7300          * The keystroke filter mask to be applied on email input
7301          * @type RegExp
7302          */
7303         'emailMask' : /[a-z0-9_\.\-@]/i,
7304
7305         /**
7306          * The function used to validate URLs
7307          * @param {String} value The URL
7308          */
7309         'url' : function(v){
7310             return url.test(v);
7311         },
7312         /**
7313          * The error text to display when the url validation function returns false
7314          * @type String
7315          */
7316         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7317         
7318         /**
7319          * The function used to validate alpha values
7320          * @param {String} value The value
7321          */
7322         'alpha' : function(v){
7323             return alpha.test(v);
7324         },
7325         /**
7326          * The error text to display when the alpha validation function returns false
7327          * @type String
7328          */
7329         'alphaText' : 'This field should only contain letters and _',
7330         /**
7331          * The keystroke filter mask to be applied on alpha input
7332          * @type RegExp
7333          */
7334         'alphaMask' : /[a-z_]/i,
7335
7336         /**
7337          * The function used to validate alphanumeric values
7338          * @param {String} value The value
7339          */
7340         'alphanum' : function(v){
7341             return alphanum.test(v);
7342         },
7343         /**
7344          * The error text to display when the alphanumeric validation function returns false
7345          * @type String
7346          */
7347         'alphanumText' : 'This field should only contain letters, numbers and _',
7348         /**
7349          * The keystroke filter mask to be applied on alphanumeric input
7350          * @type RegExp
7351          */
7352         'alphanumMask' : /[a-z0-9_]/i
7353     };
7354 }();/*
7355  * - LGPL
7356  *
7357  * Input
7358  * 
7359  */
7360
7361 /**
7362  * @class Roo.bootstrap.Input
7363  * @extends Roo.bootstrap.Component
7364  * Bootstrap Input class
7365  * @cfg {Boolean} disabled is it disabled
7366  * @cfg {String} fieldLabel - the label associated
7367  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7368  * @cfg {String} name name of the input
7369  * @cfg {string} fieldLabel - the label associated
7370  * @cfg {string}  inputType - input / file submit ...
7371  * @cfg {string} placeholder - placeholder to put in text.
7372  * @cfg {string}  before - input group add on before
7373  * @cfg {string} after - input group add on after
7374  * @cfg {string} size - (lg|sm) or leave empty..
7375  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7376  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7377  * @cfg {Number} md colspan out of 12 for computer-sized screens
7378  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7379  * @cfg {string} value default value of the input
7380  * @cfg {Number} labelWidth set the width of label (0-12)
7381  * @cfg {String} labelAlign (top|left)
7382  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7383  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7384
7385  * @cfg {String} align (left|center|right) Default left
7386  * 
7387  * 
7388  * 
7389  * @constructor
7390  * Create a new Input
7391  * @param {Object} config The config object
7392  */
7393
7394 Roo.bootstrap.Input = function(config){
7395     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7396    
7397         this.addEvents({
7398             /**
7399              * @event focus
7400              * Fires when this field receives input focus.
7401              * @param {Roo.form.Field} this
7402              */
7403             focus : true,
7404             /**
7405              * @event blur
7406              * Fires when this field loses input focus.
7407              * @param {Roo.form.Field} this
7408              */
7409             blur : true,
7410             /**
7411              * @event specialkey
7412              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7413              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7414              * @param {Roo.form.Field} this
7415              * @param {Roo.EventObject} e The event object
7416              */
7417             specialkey : true,
7418             /**
7419              * @event change
7420              * Fires just before the field blurs if the field value has changed.
7421              * @param {Roo.form.Field} this
7422              * @param {Mixed} newValue The new value
7423              * @param {Mixed} oldValue The original value
7424              */
7425             change : true,
7426             /**
7427              * @event invalid
7428              * Fires after the field has been marked as invalid.
7429              * @param {Roo.form.Field} this
7430              * @param {String} msg The validation message
7431              */
7432             invalid : true,
7433             /**
7434              * @event valid
7435              * Fires after the field has been validated with no errors.
7436              * @param {Roo.form.Field} this
7437              */
7438             valid : true,
7439              /**
7440              * @event keyup
7441              * Fires after the key up
7442              * @param {Roo.form.Field} this
7443              * @param {Roo.EventObject}  e The event Object
7444              */
7445             keyup : true
7446         });
7447 };
7448
7449 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7450      /**
7451      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7452       automatic validation (defaults to "keyup").
7453      */
7454     validationEvent : "keyup",
7455      /**
7456      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7457      */
7458     validateOnBlur : true,
7459     /**
7460      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7461      */
7462     validationDelay : 250,
7463      /**
7464      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7465      */
7466     focusClass : "x-form-focus",  // not needed???
7467     
7468        
7469     /**
7470      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7471      */
7472     invalidClass : "has-warning",
7473     
7474     /**
7475      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7476      */
7477     validClass : "has-success",
7478     
7479     /**
7480      * @cfg {Boolean} hasFeedback (true|false) default true
7481      */
7482     hasFeedback : true,
7483     
7484     /**
7485      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7486      */
7487     invalidFeedbackClass : "glyphicon-warning-sign",
7488     
7489     /**
7490      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7491      */
7492     validFeedbackClass : "glyphicon-ok",
7493     
7494     /**
7495      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7496      */
7497     selectOnFocus : false,
7498     
7499      /**
7500      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7501      */
7502     maskRe : null,
7503        /**
7504      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7505      */
7506     vtype : null,
7507     
7508       /**
7509      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7510      */
7511     disableKeyFilter : false,
7512     
7513        /**
7514      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7515      */
7516     disabled : false,
7517      /**
7518      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7519      */
7520     allowBlank : true,
7521     /**
7522      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7523      */
7524     blankText : "This field is required",
7525     
7526      /**
7527      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7528      */
7529     minLength : 0,
7530     /**
7531      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7532      */
7533     maxLength : Number.MAX_VALUE,
7534     /**
7535      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7536      */
7537     minLengthText : "The minimum length for this field is {0}",
7538     /**
7539      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7540      */
7541     maxLengthText : "The maximum length for this field is {0}",
7542   
7543     
7544     /**
7545      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7546      * If available, this function will be called only after the basic validators all return true, and will be passed the
7547      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7548      */
7549     validator : null,
7550     /**
7551      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7552      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7553      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7554      */
7555     regex : null,
7556     /**
7557      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7558      */
7559     regexText : "",
7560     
7561     autocomplete: false,
7562     
7563     
7564     fieldLabel : '',
7565     inputType : 'text',
7566     
7567     name : false,
7568     placeholder: false,
7569     before : false,
7570     after : false,
7571     size : false,
7572     hasFocus : false,
7573     preventMark: false,
7574     isFormField : true,
7575     value : '',
7576     labelWidth : 2,
7577     labelAlign : false,
7578     readOnly : false,
7579     align : false,
7580     formatedValue : false,
7581     
7582     parentLabelAlign : function()
7583     {
7584         var parent = this;
7585         while (parent.parent()) {
7586             parent = parent.parent();
7587             if (typeof(parent.labelAlign) !='undefined') {
7588                 return parent.labelAlign;
7589             }
7590         }
7591         return 'left';
7592         
7593     },
7594     
7595     getAutoCreate : function(){
7596         
7597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7598         
7599         var id = Roo.id();
7600         
7601         var cfg = {};
7602         
7603         if(this.inputType != 'hidden'){
7604             cfg.cls = 'form-group' //input-group
7605         }
7606         
7607         var input =  {
7608             tag: 'input',
7609             id : id,
7610             type : this.inputType,
7611             value : this.value,
7612             cls : 'form-control',
7613             placeholder : this.placeholder || '',
7614             autocomplete : this.autocomplete || 'new-password'
7615         };
7616         
7617         
7618         if(this.align){
7619             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7620         }
7621         
7622         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7623             input.maxLength = this.maxLength;
7624         }
7625         
7626         if (this.disabled) {
7627             input.disabled=true;
7628         }
7629         
7630         if (this.readOnly) {
7631             input.readonly=true;
7632         }
7633         
7634         if (this.name) {
7635             input.name = this.name;
7636         }
7637         if (this.size) {
7638             input.cls += ' input-' + this.size;
7639         }
7640         var settings=this;
7641         ['xs','sm','md','lg'].map(function(size){
7642             if (settings[size]) {
7643                 cfg.cls += ' col-' + size + '-' + settings[size];
7644             }
7645         });
7646         
7647         var inputblock = input;
7648         
7649         var feedback = {
7650             tag: 'span',
7651             cls: 'glyphicon form-control-feedback'
7652         };
7653             
7654         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7655             
7656             inputblock = {
7657                 cls : 'has-feedback',
7658                 cn :  [
7659                     input,
7660                     feedback
7661                 ] 
7662             };  
7663         }
7664         
7665         if (this.before || this.after) {
7666             
7667             inputblock = {
7668                 cls : 'input-group',
7669                 cn :  [] 
7670             };
7671             
7672             if (this.before && typeof(this.before) == 'string') {
7673                 
7674                 inputblock.cn.push({
7675                     tag :'span',
7676                     cls : 'roo-input-before input-group-addon',
7677                     html : this.before
7678                 });
7679             }
7680             if (this.before && typeof(this.before) == 'object') {
7681                 this.before = Roo.factory(this.before);
7682                 Roo.log(this.before);
7683                 inputblock.cn.push({
7684                     tag :'span',
7685                     cls : 'roo-input-before input-group-' +
7686                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7687                 });
7688             }
7689             
7690             inputblock.cn.push(input);
7691             
7692             if (this.after && typeof(this.after) == 'string') {
7693                 inputblock.cn.push({
7694                     tag :'span',
7695                     cls : 'roo-input-after input-group-addon',
7696                     html : this.after
7697                 });
7698             }
7699             if (this.after && typeof(this.after) == 'object') {
7700                 this.after = Roo.factory(this.after);
7701                 Roo.log(this.after);
7702                 inputblock.cn.push({
7703                     tag :'span',
7704                     cls : 'roo-input-after input-group-' +
7705                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7706                 });
7707             }
7708             
7709             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7710                 inputblock.cls += ' has-feedback';
7711                 inputblock.cn.push(feedback);
7712             }
7713         };
7714         
7715         if (align ==='left' && this.fieldLabel.length) {
7716                 Roo.log("left and has label");
7717                 cfg.cn = [
7718                     
7719                     {
7720                         tag: 'label',
7721                         'for' :  id,
7722                         cls : 'control-label col-sm-' + this.labelWidth,
7723                         html : this.fieldLabel
7724                         
7725                     },
7726                     {
7727                         cls : "col-sm-" + (12 - this.labelWidth), 
7728                         cn: [
7729                             inputblock
7730                         ]
7731                     }
7732                     
7733                 ];
7734         } else if ( this.fieldLabel.length) {
7735                 Roo.log(" label");
7736                  cfg.cn = [
7737                    
7738                     {
7739                         tag: 'label',
7740                         //cls : 'input-group-addon',
7741                         html : this.fieldLabel
7742                         
7743                     },
7744                     
7745                     inputblock
7746                     
7747                 ];
7748
7749         } else {
7750             
7751                 Roo.log(" no label && no align");
7752                 cfg.cn = [
7753                     
7754                         inputblock
7755                     
7756                 ];
7757                 
7758                 
7759         };
7760         Roo.log('input-parentType: ' + this.parentType);
7761         
7762         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7763            cfg.cls += ' navbar-form';
7764            Roo.log(cfg);
7765         }
7766         
7767         return cfg;
7768         
7769     },
7770     /**
7771      * return the real input element.
7772      */
7773     inputEl: function ()
7774     {
7775         return this.el.select('input.form-control',true).first();
7776     },
7777     
7778     tooltipEl : function()
7779     {
7780         return this.inputEl();
7781     },
7782     
7783     setDisabled : function(v)
7784     {
7785         var i  = this.inputEl().dom;
7786         if (!v) {
7787             i.removeAttribute('disabled');
7788             return;
7789             
7790         }
7791         i.setAttribute('disabled','true');
7792     },
7793     initEvents : function()
7794     {
7795           
7796         this.inputEl().on("keydown" , this.fireKey,  this);
7797         this.inputEl().on("focus", this.onFocus,  this);
7798         this.inputEl().on("blur", this.onBlur,  this);
7799         
7800         this.inputEl().relayEvent('keyup', this);
7801
7802         // reference to original value for reset
7803         this.originalValue = this.getValue();
7804         //Roo.form.TextField.superclass.initEvents.call(this);
7805         if(this.validationEvent == 'keyup'){
7806             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7807             this.inputEl().on('keyup', this.filterValidation, this);
7808         }
7809         else if(this.validationEvent !== false){
7810             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7811         }
7812         
7813         if(this.selectOnFocus){
7814             this.on("focus", this.preFocus, this);
7815             
7816         }
7817         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7818             this.inputEl().on("keypress", this.filterKeys, this);
7819         }
7820        /* if(this.grow){
7821             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7822             this.el.on("click", this.autoSize,  this);
7823         }
7824         */
7825         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7826             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7827         }
7828         
7829         if (typeof(this.before) == 'object') {
7830             this.before.render(this.el.select('.roo-input-before',true).first());
7831         }
7832         if (typeof(this.after) == 'object') {
7833             this.after.render(this.el.select('.roo-input-after',true).first());
7834         }
7835         
7836         
7837     },
7838     filterValidation : function(e){
7839         if(!e.isNavKeyPress()){
7840             this.validationTask.delay(this.validationDelay);
7841         }
7842     },
7843      /**
7844      * Validates the field value
7845      * @return {Boolean} True if the value is valid, else false
7846      */
7847     validate : function(){
7848         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7849         if(this.disabled || this.validateValue(this.getRawValue())){
7850             this.markValid();
7851             return true;
7852         }
7853         
7854         this.markInvalid();
7855         return false;
7856     },
7857     
7858     
7859     /**
7860      * Validates a value according to the field's validation rules and marks the field as invalid
7861      * if the validation fails
7862      * @param {Mixed} value The value to validate
7863      * @return {Boolean} True if the value is valid, else false
7864      */
7865     validateValue : function(value){
7866         if(value.length < 1)  { // if it's blank
7867             if(this.allowBlank){
7868                 return true;
7869             }
7870             return false;
7871         }
7872         
7873         if(value.length < this.minLength){
7874             return false;
7875         }
7876         if(value.length > this.maxLength){
7877             return false;
7878         }
7879         if(this.vtype){
7880             var vt = Roo.form.VTypes;
7881             if(!vt[this.vtype](value, this)){
7882                 return false;
7883             }
7884         }
7885         if(typeof this.validator == "function"){
7886             var msg = this.validator(value);
7887             if(msg !== true){
7888                 return false;
7889             }
7890         }
7891         
7892         if(this.regex && !this.regex.test(value)){
7893             return false;
7894         }
7895         
7896         return true;
7897     },
7898
7899     
7900     
7901      // private
7902     fireKey : function(e){
7903         //Roo.log('field ' + e.getKey());
7904         if(e.isNavKeyPress()){
7905             this.fireEvent("specialkey", this, e);
7906         }
7907     },
7908     focus : function (selectText){
7909         if(this.rendered){
7910             this.inputEl().focus();
7911             if(selectText === true){
7912                 this.inputEl().dom.select();
7913             }
7914         }
7915         return this;
7916     } ,
7917     
7918     onFocus : function(){
7919         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7920            // this.el.addClass(this.focusClass);
7921         }
7922         if(!this.hasFocus){
7923             this.hasFocus = true;
7924             this.startValue = this.getValue();
7925             this.fireEvent("focus", this);
7926         }
7927     },
7928     
7929     beforeBlur : Roo.emptyFn,
7930
7931     
7932     // private
7933     onBlur : function(){
7934         this.beforeBlur();
7935         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7936             //this.el.removeClass(this.focusClass);
7937         }
7938         this.hasFocus = false;
7939         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7940             this.validate();
7941         }
7942         var v = this.getValue();
7943         if(String(v) !== String(this.startValue)){
7944             this.fireEvent('change', this, v, this.startValue);
7945         }
7946         this.fireEvent("blur", this);
7947     },
7948     
7949     /**
7950      * Resets the current field value to the originally loaded value and clears any validation messages
7951      */
7952     reset : function(){
7953         this.setValue(this.originalValue);
7954         this.validate();
7955     },
7956      /**
7957      * Returns the name of the field
7958      * @return {Mixed} name The name field
7959      */
7960     getName: function(){
7961         return this.name;
7962     },
7963      /**
7964      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7965      * @return {Mixed} value The field value
7966      */
7967     getValue : function(){
7968         
7969         var v = this.inputEl().getValue();
7970         
7971         return v;
7972     },
7973     /**
7974      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7975      * @return {Mixed} value The field value
7976      */
7977     getRawValue : function(){
7978         var v = this.inputEl().getValue();
7979         
7980         return v;
7981     },
7982     
7983     /**
7984      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7985      * @param {Mixed} value The value to set
7986      */
7987     setRawValue : function(v){
7988         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7989     },
7990     
7991     selectText : function(start, end){
7992         var v = this.getRawValue();
7993         if(v.length > 0){
7994             start = start === undefined ? 0 : start;
7995             end = end === undefined ? v.length : end;
7996             var d = this.inputEl().dom;
7997             if(d.setSelectionRange){
7998                 d.setSelectionRange(start, end);
7999             }else if(d.createTextRange){
8000                 var range = d.createTextRange();
8001                 range.moveStart("character", start);
8002                 range.moveEnd("character", v.length-end);
8003                 range.select();
8004             }
8005         }
8006     },
8007     
8008     /**
8009      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8010      * @param {Mixed} value The value to set
8011      */
8012     setValue : function(v){
8013         this.value = v;
8014         if(this.rendered){
8015             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8016             this.validate();
8017         }
8018     },
8019     
8020     /*
8021     processValue : function(value){
8022         if(this.stripCharsRe){
8023             var newValue = value.replace(this.stripCharsRe, '');
8024             if(newValue !== value){
8025                 this.setRawValue(newValue);
8026                 return newValue;
8027             }
8028         }
8029         return value;
8030     },
8031   */
8032     preFocus : function(){
8033         
8034         if(this.selectOnFocus){
8035             this.inputEl().dom.select();
8036         }
8037     },
8038     filterKeys : function(e){
8039         var k = e.getKey();
8040         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8041             return;
8042         }
8043         var c = e.getCharCode(), cc = String.fromCharCode(c);
8044         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8045             return;
8046         }
8047         if(!this.maskRe.test(cc)){
8048             e.stopEvent();
8049         }
8050     },
8051      /**
8052      * Clear any invalid styles/messages for this field
8053      */
8054     clearInvalid : function(){
8055         
8056         if(!this.el || this.preventMark){ // not rendered
8057             return;
8058         }
8059         this.el.removeClass(this.invalidClass);
8060         
8061         this.fireEvent('valid', this);
8062     },
8063     
8064      /**
8065      * Mark this field as valid
8066      */
8067     markValid : function(){
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.validClass);
8079         
8080         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8087             }
8088             
8089         }
8090         
8091         this.fireEvent('valid', this);
8092     },
8093     
8094      /**
8095      * Mark this field as invalid
8096      * @param {String} msg The validation message
8097      */
8098     markInvalid : function(msg){
8099         if(!this.el  || this.preventMark){ // not rendered
8100             return;
8101         }
8102         
8103         this.el.removeClass([this.invalidClass, this.validClass]);
8104         
8105         if(this.disabled || this.allowBlank){
8106             return;
8107         }
8108         
8109         this.el.addClass(this.invalidClass);
8110         
8111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8112             
8113             var feedback = this.el.select('.form-control-feedback', true).first();
8114             
8115             if(feedback){
8116                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8117                 
8118                 if(this.getValue().length){
8119                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8120                 }
8121                 
8122             }
8123             
8124         }
8125         
8126         this.fireEvent('invalid', this, msg);
8127     },
8128     // private
8129     SafariOnKeyDown : function(event)
8130     {
8131         // this is a workaround for a password hang bug on chrome/ webkit.
8132         
8133         var isSelectAll = false;
8134         
8135         if(this.inputEl().dom.selectionEnd > 0){
8136             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8137         }
8138         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8139             event.preventDefault();
8140             this.setValue('');
8141             return;
8142         }
8143         
8144         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8145             
8146             event.preventDefault();
8147             // this is very hacky as keydown always get's upper case.
8148             //
8149             var cc = String.fromCharCode(event.getCharCode());
8150             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8151             
8152         }
8153     },
8154     adjustWidth : function(tag, w){
8155         tag = tag.toLowerCase();
8156         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8157             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8158                 if(tag == 'input'){
8159                     return w + 2;
8160                 }
8161                 if(tag == 'textarea'){
8162                     return w-2;
8163                 }
8164             }else if(Roo.isOpera){
8165                 if(tag == 'input'){
8166                     return w + 2;
8167                 }
8168                 if(tag == 'textarea'){
8169                     return w-2;
8170                 }
8171             }
8172         }
8173         return w;
8174     }
8175     
8176 });
8177
8178  
8179 /*
8180  * - LGPL
8181  *
8182  * Input
8183  * 
8184  */
8185
8186 /**
8187  * @class Roo.bootstrap.TextArea
8188  * @extends Roo.bootstrap.Input
8189  * Bootstrap TextArea class
8190  * @cfg {Number} cols Specifies the visible width of a text area
8191  * @cfg {Number} rows Specifies the visible number of lines in a text area
8192  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8193  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8194  * @cfg {string} html text
8195  * 
8196  * @constructor
8197  * Create a new TextArea
8198  * @param {Object} config The config object
8199  */
8200
8201 Roo.bootstrap.TextArea = function(config){
8202     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8203    
8204 };
8205
8206 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8207      
8208     cols : false,
8209     rows : 5,
8210     readOnly : false,
8211     warp : 'soft',
8212     resize : false,
8213     value: false,
8214     html: false,
8215     
8216     getAutoCreate : function(){
8217         
8218         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8219         
8220         var id = Roo.id();
8221         
8222         var cfg = {};
8223         
8224         var input =  {
8225             tag: 'textarea',
8226             id : id,
8227             warp : this.warp,
8228             rows : this.rows,
8229             value : this.value || '',
8230             html: this.html || '',
8231             cls : 'form-control',
8232             placeholder : this.placeholder || '' 
8233             
8234         };
8235         
8236         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8237             input.maxLength = this.maxLength;
8238         }
8239         
8240         if(this.resize){
8241             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8242         }
8243         
8244         if(this.cols){
8245             input.cols = this.cols;
8246         }
8247         
8248         if (this.readOnly) {
8249             input.readonly = true;
8250         }
8251         
8252         if (this.name) {
8253             input.name = this.name;
8254         }
8255         
8256         if (this.size) {
8257             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8258         }
8259         
8260         var settings=this;
8261         ['xs','sm','md','lg'].map(function(size){
8262             if (settings[size]) {
8263                 cfg.cls += ' col-' + size + '-' + settings[size];
8264             }
8265         });
8266         
8267         var inputblock = input;
8268         
8269         if(this.hasFeedback && !this.allowBlank){
8270             
8271             var feedback = {
8272                 tag: 'span',
8273                 cls: 'glyphicon form-control-feedback'
8274             };
8275
8276             inputblock = {
8277                 cls : 'has-feedback',
8278                 cn :  [
8279                     input,
8280                     feedback
8281                 ] 
8282             };  
8283         }
8284         
8285         
8286         if (this.before || this.after) {
8287             
8288             inputblock = {
8289                 cls : 'input-group',
8290                 cn :  [] 
8291             };
8292             if (this.before) {
8293                 inputblock.cn.push({
8294                     tag :'span',
8295                     cls : 'input-group-addon',
8296                     html : this.before
8297                 });
8298             }
8299             
8300             inputblock.cn.push(input);
8301             
8302             if(this.hasFeedback && !this.allowBlank){
8303                 inputblock.cls += ' has-feedback';
8304                 inputblock.cn.push(feedback);
8305             }
8306             
8307             if (this.after) {
8308                 inputblock.cn.push({
8309                     tag :'span',
8310                     cls : 'input-group-addon',
8311                     html : this.after
8312                 });
8313             }
8314             
8315         }
8316         
8317         if (align ==='left' && this.fieldLabel.length) {
8318                 Roo.log("left and has label");
8319                 cfg.cn = [
8320                     
8321                     {
8322                         tag: 'label',
8323                         'for' :  id,
8324                         cls : 'control-label col-sm-' + this.labelWidth,
8325                         html : this.fieldLabel
8326                         
8327                     },
8328                     {
8329                         cls : "col-sm-" + (12 - this.labelWidth), 
8330                         cn: [
8331                             inputblock
8332                         ]
8333                     }
8334                     
8335                 ];
8336         } else if ( this.fieldLabel.length) {
8337                 Roo.log(" label");
8338                  cfg.cn = [
8339                    
8340                     {
8341                         tag: 'label',
8342                         //cls : 'input-group-addon',
8343                         html : this.fieldLabel
8344                         
8345                     },
8346                     
8347                     inputblock
8348                     
8349                 ];
8350
8351         } else {
8352             
8353                    Roo.log(" no label && no align");
8354                 cfg.cn = [
8355                     
8356                         inputblock
8357                     
8358                 ];
8359                 
8360                 
8361         }
8362         
8363         if (this.disabled) {
8364             input.disabled=true;
8365         }
8366         
8367         return cfg;
8368         
8369     },
8370     /**
8371      * return the real textarea element.
8372      */
8373     inputEl: function ()
8374     {
8375         return this.el.select('textarea.form-control',true).first();
8376     }
8377 });
8378
8379  
8380 /*
8381  * - LGPL
8382  *
8383  * trigger field - base class for combo..
8384  * 
8385  */
8386  
8387 /**
8388  * @class Roo.bootstrap.TriggerField
8389  * @extends Roo.bootstrap.Input
8390  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8391  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8392  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8393  * for which you can provide a custom implementation.  For example:
8394  * <pre><code>
8395 var trigger = new Roo.bootstrap.TriggerField();
8396 trigger.onTriggerClick = myTriggerFn;
8397 trigger.applyTo('my-field');
8398 </code></pre>
8399  *
8400  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8401  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8402  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8403  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8404  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8405
8406  * @constructor
8407  * Create a new TriggerField.
8408  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8409  * to the base TextField)
8410  */
8411 Roo.bootstrap.TriggerField = function(config){
8412     this.mimicing = false;
8413     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8414 };
8415
8416 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8417     /**
8418      * @cfg {String} triggerClass A CSS class to apply to the trigger
8419      */
8420      /**
8421      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8422      */
8423     hideTrigger:false,
8424
8425     /** @cfg {Boolean} grow @hide */
8426     /** @cfg {Number} growMin @hide */
8427     /** @cfg {Number} growMax @hide */
8428
8429     /**
8430      * @hide 
8431      * @method
8432      */
8433     autoSize: Roo.emptyFn,
8434     // private
8435     monitorTab : true,
8436     // private
8437     deferHeight : true,
8438
8439     
8440     actionMode : 'wrap',
8441     
8442     caret : false,
8443     
8444     
8445     getAutoCreate : function(){
8446        
8447         var align = this.labelAlign || this.parentLabelAlign();
8448         
8449         var id = Roo.id();
8450         
8451         var cfg = {
8452             cls: 'form-group' //input-group
8453         };
8454         
8455         
8456         var input =  {
8457             tag: 'input',
8458             id : id,
8459             type : this.inputType,
8460             cls : 'form-control',
8461             autocomplete: 'new-password',
8462             placeholder : this.placeholder || '' 
8463             
8464         };
8465         if (this.name) {
8466             input.name = this.name;
8467         }
8468         if (this.size) {
8469             input.cls += ' input-' + this.size;
8470         }
8471         
8472         if (this.disabled) {
8473             input.disabled=true;
8474         }
8475         
8476         var inputblock = input;
8477         
8478         if(this.hasFeedback && !this.allowBlank){
8479             
8480             var feedback = {
8481                 tag: 'span',
8482                 cls: 'glyphicon form-control-feedback'
8483             };
8484
8485             inputblock = {
8486                 cls : 'has-feedback',
8487                 cn :  [
8488                     input,
8489                     feedback
8490                 ] 
8491             };  
8492         }
8493         
8494         if (this.before || this.after) {
8495             
8496             inputblock = {
8497                 cls : 'input-group',
8498                 cn :  [] 
8499             };
8500             if (this.before) {
8501                 inputblock.cn.push({
8502                     tag :'span',
8503                     cls : 'input-group-addon',
8504                     html : this.before
8505                 });
8506             }
8507             
8508             inputblock.cn.push(input);
8509             
8510             if(this.hasFeedback && !this.allowBlank){
8511                 inputblock.cls += ' has-feedback';
8512                 inputblock.cn.push(feedback);
8513             }
8514             
8515             if (this.after) {
8516                 inputblock.cn.push({
8517                     tag :'span',
8518                     cls : 'input-group-addon',
8519                     html : this.after
8520                 });
8521             }
8522             
8523         };
8524         
8525         var box = {
8526             tag: 'div',
8527             cn: [
8528                 {
8529                     tag: 'input',
8530                     type : 'hidden',
8531                     cls: 'form-hidden-field'
8532                 },
8533                 inputblock
8534             ]
8535             
8536         };
8537         
8538         if(this.multiple){
8539             Roo.log('multiple');
8540             
8541             box = {
8542                 tag: 'div',
8543                 cn: [
8544                     {
8545                         tag: 'input',
8546                         type : 'hidden',
8547                         cls: 'form-hidden-field'
8548                     },
8549                     {
8550                         tag: 'ul',
8551                         cls: 'select2-choices',
8552                         cn:[
8553                             {
8554                                 tag: 'li',
8555                                 cls: 'select2-search-field',
8556                                 cn: [
8557
8558                                     inputblock
8559                                 ]
8560                             }
8561                         ]
8562                     }
8563                 ]
8564             }
8565         };
8566         
8567         var combobox = {
8568             cls: 'select2-container input-group',
8569             cn: [
8570                 box
8571 //                {
8572 //                    tag: 'ul',
8573 //                    cls: 'typeahead typeahead-long dropdown-menu',
8574 //                    style: 'display:none'
8575 //                }
8576             ]
8577         };
8578         
8579         if(!this.multiple && this.showToggleBtn){
8580             
8581             var caret = {
8582                         tag: 'span',
8583                         cls: 'caret'
8584              };
8585             if (this.caret != false) {
8586                 caret = {
8587                      tag: 'i',
8588                      cls: 'fa fa-' + this.caret
8589                 };
8590                 
8591             }
8592             
8593             combobox.cn.push({
8594                 tag :'span',
8595                 cls : 'input-group-addon btn dropdown-toggle',
8596                 cn : [
8597                     caret,
8598                     {
8599                         tag: 'span',
8600                         cls: 'combobox-clear',
8601                         cn  : [
8602                             {
8603                                 tag : 'i',
8604                                 cls: 'icon-remove'
8605                             }
8606                         ]
8607                     }
8608                 ]
8609
8610             })
8611         }
8612         
8613         if(this.multiple){
8614             combobox.cls += ' select2-container-multi';
8615         }
8616         
8617         if (align ==='left' && this.fieldLabel.length) {
8618             
8619                 Roo.log("left and has label");
8620                 cfg.cn = [
8621                     
8622                     {
8623                         tag: 'label',
8624                         'for' :  id,
8625                         cls : 'control-label col-sm-' + this.labelWidth,
8626                         html : this.fieldLabel
8627                         
8628                     },
8629                     {
8630                         cls : "col-sm-" + (12 - this.labelWidth), 
8631                         cn: [
8632                             combobox
8633                         ]
8634                     }
8635                     
8636                 ];
8637         } else if ( this.fieldLabel.length) {
8638                 Roo.log(" label");
8639                  cfg.cn = [
8640                    
8641                     {
8642                         tag: 'label',
8643                         //cls : 'input-group-addon',
8644                         html : this.fieldLabel
8645                         
8646                     },
8647                     
8648                     combobox
8649                     
8650                 ];
8651
8652         } else {
8653             
8654                 Roo.log(" no label && no align");
8655                 cfg = combobox
8656                      
8657                 
8658         }
8659          
8660         var settings=this;
8661         ['xs','sm','md','lg'].map(function(size){
8662             if (settings[size]) {
8663                 cfg.cls += ' col-' + size + '-' + settings[size];
8664             }
8665         });
8666         
8667         return cfg;
8668         
8669     },
8670     
8671     
8672     
8673     // private
8674     onResize : function(w, h){
8675 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8676 //        if(typeof w == 'number'){
8677 //            var x = w - this.trigger.getWidth();
8678 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8679 //            this.trigger.setStyle('left', x+'px');
8680 //        }
8681     },
8682
8683     // private
8684     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8685
8686     // private
8687     getResizeEl : function(){
8688         return this.inputEl();
8689     },
8690
8691     // private
8692     getPositionEl : function(){
8693         return this.inputEl();
8694     },
8695
8696     // private
8697     alignErrorIcon : function(){
8698         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8699     },
8700
8701     // private
8702     initEvents : function(){
8703         
8704         this.createList();
8705         
8706         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8707         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8708         if(!this.multiple && this.showToggleBtn){
8709             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8710             if(this.hideTrigger){
8711                 this.trigger.setDisplayed(false);
8712             }
8713             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8714         }
8715         
8716         if(this.multiple){
8717             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8718         }
8719         
8720         //this.trigger.addClassOnOver('x-form-trigger-over');
8721         //this.trigger.addClassOnClick('x-form-trigger-click');
8722         
8723         //if(!this.width){
8724         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8725         //}
8726     },
8727     
8728     createList : function()
8729     {
8730         this.list = Roo.get(document.body).createChild({
8731             tag: 'ul',
8732             cls: 'typeahead typeahead-long dropdown-menu',
8733             style: 'display:none'
8734         });
8735         
8736         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8737         
8738     },
8739
8740     // private
8741     initTrigger : function(){
8742        
8743     },
8744
8745     // private
8746     onDestroy : function(){
8747         if(this.trigger){
8748             this.trigger.removeAllListeners();
8749           //  this.trigger.remove();
8750         }
8751         //if(this.wrap){
8752         //    this.wrap.remove();
8753         //}
8754         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8755     },
8756
8757     // private
8758     onFocus : function(){
8759         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8760         /*
8761         if(!this.mimicing){
8762             this.wrap.addClass('x-trigger-wrap-focus');
8763             this.mimicing = true;
8764             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8765             if(this.monitorTab){
8766                 this.el.on("keydown", this.checkTab, this);
8767             }
8768         }
8769         */
8770     },
8771
8772     // private
8773     checkTab : function(e){
8774         if(e.getKey() == e.TAB){
8775             this.triggerBlur();
8776         }
8777     },
8778
8779     // private
8780     onBlur : function(){
8781         // do nothing
8782     },
8783
8784     // private
8785     mimicBlur : function(e, t){
8786         /*
8787         if(!this.wrap.contains(t) && this.validateBlur()){
8788             this.triggerBlur();
8789         }
8790         */
8791     },
8792
8793     // private
8794     triggerBlur : function(){
8795         this.mimicing = false;
8796         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8797         if(this.monitorTab){
8798             this.el.un("keydown", this.checkTab, this);
8799         }
8800         //this.wrap.removeClass('x-trigger-wrap-focus');
8801         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8802     },
8803
8804     // private
8805     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8806     validateBlur : function(e, t){
8807         return true;
8808     },
8809
8810     // private
8811     onDisable : function(){
8812         this.inputEl().dom.disabled = true;
8813         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8814         //if(this.wrap){
8815         //    this.wrap.addClass('x-item-disabled');
8816         //}
8817     },
8818
8819     // private
8820     onEnable : function(){
8821         this.inputEl().dom.disabled = false;
8822         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8823         //if(this.wrap){
8824         //    this.el.removeClass('x-item-disabled');
8825         //}
8826     },
8827
8828     // private
8829     onShow : function(){
8830         var ae = this.getActionEl();
8831         
8832         if(ae){
8833             ae.dom.style.display = '';
8834             ae.dom.style.visibility = 'visible';
8835         }
8836     },
8837
8838     // private
8839     
8840     onHide : function(){
8841         var ae = this.getActionEl();
8842         ae.dom.style.display = 'none';
8843     },
8844
8845     /**
8846      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8847      * by an implementing function.
8848      * @method
8849      * @param {EventObject} e
8850      */
8851     onTriggerClick : Roo.emptyFn
8852 });
8853  /*
8854  * Based on:
8855  * Ext JS Library 1.1.1
8856  * Copyright(c) 2006-2007, Ext JS, LLC.
8857  *
8858  * Originally Released Under LGPL - original licence link has changed is not relivant.
8859  *
8860  * Fork - LGPL
8861  * <script type="text/javascript">
8862  */
8863
8864
8865 /**
8866  * @class Roo.data.SortTypes
8867  * @singleton
8868  * Defines the default sorting (casting?) comparison functions used when sorting data.
8869  */
8870 Roo.data.SortTypes = {
8871     /**
8872      * Default sort that does nothing
8873      * @param {Mixed} s The value being converted
8874      * @return {Mixed} The comparison value
8875      */
8876     none : function(s){
8877         return s;
8878     },
8879     
8880     /**
8881      * The regular expression used to strip tags
8882      * @type {RegExp}
8883      * @property
8884      */
8885     stripTagsRE : /<\/?[^>]+>/gi,
8886     
8887     /**
8888      * Strips all HTML tags to sort on text only
8889      * @param {Mixed} s The value being converted
8890      * @return {String} The comparison value
8891      */
8892     asText : function(s){
8893         return String(s).replace(this.stripTagsRE, "");
8894     },
8895     
8896     /**
8897      * Strips all HTML tags to sort on text only - Case insensitive
8898      * @param {Mixed} s The value being converted
8899      * @return {String} The comparison value
8900      */
8901     asUCText : function(s){
8902         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8903     },
8904     
8905     /**
8906      * Case insensitive string
8907      * @param {Mixed} s The value being converted
8908      * @return {String} The comparison value
8909      */
8910     asUCString : function(s) {
8911         return String(s).toUpperCase();
8912     },
8913     
8914     /**
8915      * Date sorting
8916      * @param {Mixed} s The value being converted
8917      * @return {Number} The comparison value
8918      */
8919     asDate : function(s) {
8920         if(!s){
8921             return 0;
8922         }
8923         if(s instanceof Date){
8924             return s.getTime();
8925         }
8926         return Date.parse(String(s));
8927     },
8928     
8929     /**
8930      * Float sorting
8931      * @param {Mixed} s The value being converted
8932      * @return {Float} The comparison value
8933      */
8934     asFloat : function(s) {
8935         var val = parseFloat(String(s).replace(/,/g, ""));
8936         if(isNaN(val)) val = 0;
8937         return val;
8938     },
8939     
8940     /**
8941      * Integer sorting
8942      * @param {Mixed} s The value being converted
8943      * @return {Number} The comparison value
8944      */
8945     asInt : function(s) {
8946         var val = parseInt(String(s).replace(/,/g, ""));
8947         if(isNaN(val)) val = 0;
8948         return val;
8949     }
8950 };/*
8951  * Based on:
8952  * Ext JS Library 1.1.1
8953  * Copyright(c) 2006-2007, Ext JS, LLC.
8954  *
8955  * Originally Released Under LGPL - original licence link has changed is not relivant.
8956  *
8957  * Fork - LGPL
8958  * <script type="text/javascript">
8959  */
8960
8961 /**
8962 * @class Roo.data.Record
8963  * Instances of this class encapsulate both record <em>definition</em> information, and record
8964  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8965  * to access Records cached in an {@link Roo.data.Store} object.<br>
8966  * <p>
8967  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8968  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8969  * objects.<br>
8970  * <p>
8971  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8972  * @constructor
8973  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8974  * {@link #create}. The parameters are the same.
8975  * @param {Array} data An associative Array of data values keyed by the field name.
8976  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8977  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8978  * not specified an integer id is generated.
8979  */
8980 Roo.data.Record = function(data, id){
8981     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8982     this.data = data;
8983 };
8984
8985 /**
8986  * Generate a constructor for a specific record layout.
8987  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8988  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8989  * Each field definition object may contain the following properties: <ul>
8990  * <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,
8991  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8992  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8993  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8994  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8995  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8996  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8997  * this may be omitted.</p></li>
8998  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8999  * <ul><li>auto (Default, implies no conversion)</li>
9000  * <li>string</li>
9001  * <li>int</li>
9002  * <li>float</li>
9003  * <li>boolean</li>
9004  * <li>date</li></ul></p></li>
9005  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9006  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9007  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9008  * by the Reader into an object that will be stored in the Record. It is passed the
9009  * following parameters:<ul>
9010  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9011  * </ul></p></li>
9012  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9013  * </ul>
9014  * <br>usage:<br><pre><code>
9015 var TopicRecord = Roo.data.Record.create(
9016     {name: 'title', mapping: 'topic_title'},
9017     {name: 'author', mapping: 'username'},
9018     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9019     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9020     {name: 'lastPoster', mapping: 'user2'},
9021     {name: 'excerpt', mapping: 'post_text'}
9022 );
9023
9024 var myNewRecord = new TopicRecord({
9025     title: 'Do my job please',
9026     author: 'noobie',
9027     totalPosts: 1,
9028     lastPost: new Date(),
9029     lastPoster: 'Animal',
9030     excerpt: 'No way dude!'
9031 });
9032 myStore.add(myNewRecord);
9033 </code></pre>
9034  * @method create
9035  * @static
9036  */
9037 Roo.data.Record.create = function(o){
9038     var f = function(){
9039         f.superclass.constructor.apply(this, arguments);
9040     };
9041     Roo.extend(f, Roo.data.Record);
9042     var p = f.prototype;
9043     p.fields = new Roo.util.MixedCollection(false, function(field){
9044         return field.name;
9045     });
9046     for(var i = 0, len = o.length; i < len; i++){
9047         p.fields.add(new Roo.data.Field(o[i]));
9048     }
9049     f.getField = function(name){
9050         return p.fields.get(name);  
9051     };
9052     return f;
9053 };
9054
9055 Roo.data.Record.AUTO_ID = 1000;
9056 Roo.data.Record.EDIT = 'edit';
9057 Roo.data.Record.REJECT = 'reject';
9058 Roo.data.Record.COMMIT = 'commit';
9059
9060 Roo.data.Record.prototype = {
9061     /**
9062      * Readonly flag - true if this record has been modified.
9063      * @type Boolean
9064      */
9065     dirty : false,
9066     editing : false,
9067     error: null,
9068     modified: null,
9069
9070     // private
9071     join : function(store){
9072         this.store = store;
9073     },
9074
9075     /**
9076      * Set the named field to the specified value.
9077      * @param {String} name The name of the field to set.
9078      * @param {Object} value The value to set the field to.
9079      */
9080     set : function(name, value){
9081         if(this.data[name] == value){
9082             return;
9083         }
9084         this.dirty = true;
9085         if(!this.modified){
9086             this.modified = {};
9087         }
9088         if(typeof this.modified[name] == 'undefined'){
9089             this.modified[name] = this.data[name];
9090         }
9091         this.data[name] = value;
9092         if(!this.editing && this.store){
9093             this.store.afterEdit(this);
9094         }       
9095     },
9096
9097     /**
9098      * Get the value of the named field.
9099      * @param {String} name The name of the field to get the value of.
9100      * @return {Object} The value of the field.
9101      */
9102     get : function(name){
9103         return this.data[name]; 
9104     },
9105
9106     // private
9107     beginEdit : function(){
9108         this.editing = true;
9109         this.modified = {}; 
9110     },
9111
9112     // private
9113     cancelEdit : function(){
9114         this.editing = false;
9115         delete this.modified;
9116     },
9117
9118     // private
9119     endEdit : function(){
9120         this.editing = false;
9121         if(this.dirty && this.store){
9122             this.store.afterEdit(this);
9123         }
9124     },
9125
9126     /**
9127      * Usually called by the {@link Roo.data.Store} which owns the Record.
9128      * Rejects all changes made to the Record since either creation, or the last commit operation.
9129      * Modified fields are reverted to their original values.
9130      * <p>
9131      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9132      * of reject operations.
9133      */
9134     reject : function(){
9135         var m = this.modified;
9136         for(var n in m){
9137             if(typeof m[n] != "function"){
9138                 this.data[n] = m[n];
9139             }
9140         }
9141         this.dirty = false;
9142         delete this.modified;
9143         this.editing = false;
9144         if(this.store){
9145             this.store.afterReject(this);
9146         }
9147     },
9148
9149     /**
9150      * Usually called by the {@link Roo.data.Store} which owns the Record.
9151      * Commits all changes made to the Record since either creation, or the last commit operation.
9152      * <p>
9153      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9154      * of commit operations.
9155      */
9156     commit : function(){
9157         this.dirty = false;
9158         delete this.modified;
9159         this.editing = false;
9160         if(this.store){
9161             this.store.afterCommit(this);
9162         }
9163     },
9164
9165     // private
9166     hasError : function(){
9167         return this.error != null;
9168     },
9169
9170     // private
9171     clearError : function(){
9172         this.error = null;
9173     },
9174
9175     /**
9176      * Creates a copy of this record.
9177      * @param {String} id (optional) A new record id if you don't want to use this record's id
9178      * @return {Record}
9179      */
9180     copy : function(newId) {
9181         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9182     }
9183 };/*
9184  * Based on:
9185  * Ext JS Library 1.1.1
9186  * Copyright(c) 2006-2007, Ext JS, LLC.
9187  *
9188  * Originally Released Under LGPL - original licence link has changed is not relivant.
9189  *
9190  * Fork - LGPL
9191  * <script type="text/javascript">
9192  */
9193
9194
9195
9196 /**
9197  * @class Roo.data.Store
9198  * @extends Roo.util.Observable
9199  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9200  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9201  * <p>
9202  * 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
9203  * has no knowledge of the format of the data returned by the Proxy.<br>
9204  * <p>
9205  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9206  * instances from the data object. These records are cached and made available through accessor functions.
9207  * @constructor
9208  * Creates a new Store.
9209  * @param {Object} config A config object containing the objects needed for the Store to access data,
9210  * and read the data into Records.
9211  */
9212 Roo.data.Store = function(config){
9213     this.data = new Roo.util.MixedCollection(false);
9214     this.data.getKey = function(o){
9215         return o.id;
9216     };
9217     this.baseParams = {};
9218     // private
9219     this.paramNames = {
9220         "start" : "start",
9221         "limit" : "limit",
9222         "sort" : "sort",
9223         "dir" : "dir",
9224         "multisort" : "_multisort"
9225     };
9226
9227     if(config && config.data){
9228         this.inlineData = config.data;
9229         delete config.data;
9230     }
9231
9232     Roo.apply(this, config);
9233     
9234     if(this.reader){ // reader passed
9235         this.reader = Roo.factory(this.reader, Roo.data);
9236         this.reader.xmodule = this.xmodule || false;
9237         if(!this.recordType){
9238             this.recordType = this.reader.recordType;
9239         }
9240         if(this.reader.onMetaChange){
9241             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9242         }
9243     }
9244
9245     if(this.recordType){
9246         this.fields = this.recordType.prototype.fields;
9247     }
9248     this.modified = [];
9249
9250     this.addEvents({
9251         /**
9252          * @event datachanged
9253          * Fires when the data cache has changed, and a widget which is using this Store
9254          * as a Record cache should refresh its view.
9255          * @param {Store} this
9256          */
9257         datachanged : true,
9258         /**
9259          * @event metachange
9260          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9261          * @param {Store} this
9262          * @param {Object} meta The JSON metadata
9263          */
9264         metachange : true,
9265         /**
9266          * @event add
9267          * Fires when Records have been added to the Store
9268          * @param {Store} this
9269          * @param {Roo.data.Record[]} records The array of Records added
9270          * @param {Number} index The index at which the record(s) were added
9271          */
9272         add : true,
9273         /**
9274          * @event remove
9275          * Fires when a Record has been removed from the Store
9276          * @param {Store} this
9277          * @param {Roo.data.Record} record The Record that was removed
9278          * @param {Number} index The index at which the record was removed
9279          */
9280         remove : true,
9281         /**
9282          * @event update
9283          * Fires when a Record has been updated
9284          * @param {Store} this
9285          * @param {Roo.data.Record} record The Record that was updated
9286          * @param {String} operation The update operation being performed.  Value may be one of:
9287          * <pre><code>
9288  Roo.data.Record.EDIT
9289  Roo.data.Record.REJECT
9290  Roo.data.Record.COMMIT
9291          * </code></pre>
9292          */
9293         update : true,
9294         /**
9295          * @event clear
9296          * Fires when the data cache has been cleared.
9297          * @param {Store} this
9298          */
9299         clear : true,
9300         /**
9301          * @event beforeload
9302          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9303          * the load action will be canceled.
9304          * @param {Store} this
9305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9306          */
9307         beforeload : true,
9308         /**
9309          * @event beforeloadadd
9310          * Fires after a new set of Records has been loaded.
9311          * @param {Store} this
9312          * @param {Roo.data.Record[]} records The Records that were loaded
9313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9314          */
9315         beforeloadadd : true,
9316         /**
9317          * @event load
9318          * Fires after a new set of Records has been loaded, before they are added to the store.
9319          * @param {Store} this
9320          * @param {Roo.data.Record[]} records The Records that were loaded
9321          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9322          * @params {Object} return from reader
9323          */
9324         load : true,
9325         /**
9326          * @event loadexception
9327          * Fires if an exception occurs in the Proxy during loading.
9328          * Called with the signature of the Proxy's "loadexception" event.
9329          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9330          * 
9331          * @param {Proxy} 
9332          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9333          * @param {Object} load options 
9334          * @param {Object} jsonData from your request (normally this contains the Exception)
9335          */
9336         loadexception : true
9337     });
9338     
9339     if(this.proxy){
9340         this.proxy = Roo.factory(this.proxy, Roo.data);
9341         this.proxy.xmodule = this.xmodule || false;
9342         this.relayEvents(this.proxy,  ["loadexception"]);
9343     }
9344     this.sortToggle = {};
9345     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9346
9347     Roo.data.Store.superclass.constructor.call(this);
9348
9349     if(this.inlineData){
9350         this.loadData(this.inlineData);
9351         delete this.inlineData;
9352     }
9353 };
9354
9355 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9356      /**
9357     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9358     * without a remote query - used by combo/forms at present.
9359     */
9360     
9361     /**
9362     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9363     */
9364     /**
9365     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9366     */
9367     /**
9368     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9369     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9370     */
9371     /**
9372     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9373     * on any HTTP request
9374     */
9375     /**
9376     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9377     */
9378     /**
9379     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9380     */
9381     multiSort: false,
9382     /**
9383     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9384     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9385     */
9386     remoteSort : false,
9387
9388     /**
9389     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9390      * loaded or when a record is removed. (defaults to false).
9391     */
9392     pruneModifiedRecords : false,
9393
9394     // private
9395     lastOptions : null,
9396
9397     /**
9398      * Add Records to the Store and fires the add event.
9399      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9400      */
9401     add : function(records){
9402         records = [].concat(records);
9403         for(var i = 0, len = records.length; i < len; i++){
9404             records[i].join(this);
9405         }
9406         var index = this.data.length;
9407         this.data.addAll(records);
9408         this.fireEvent("add", this, records, index);
9409     },
9410
9411     /**
9412      * Remove a Record from the Store and fires the remove event.
9413      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9414      */
9415     remove : function(record){
9416         var index = this.data.indexOf(record);
9417         this.data.removeAt(index);
9418         if(this.pruneModifiedRecords){
9419             this.modified.remove(record);
9420         }
9421         this.fireEvent("remove", this, record, index);
9422     },
9423
9424     /**
9425      * Remove all Records from the Store and fires the clear event.
9426      */
9427     removeAll : function(){
9428         this.data.clear();
9429         if(this.pruneModifiedRecords){
9430             this.modified = [];
9431         }
9432         this.fireEvent("clear", this);
9433     },
9434
9435     /**
9436      * Inserts Records to the Store at the given index and fires the add event.
9437      * @param {Number} index The start index at which to insert the passed Records.
9438      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9439      */
9440     insert : function(index, records){
9441         records = [].concat(records);
9442         for(var i = 0, len = records.length; i < len; i++){
9443             this.data.insert(index, records[i]);
9444             records[i].join(this);
9445         }
9446         this.fireEvent("add", this, records, index);
9447     },
9448
9449     /**
9450      * Get the index within the cache of the passed Record.
9451      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9452      * @return {Number} The index of the passed Record. Returns -1 if not found.
9453      */
9454     indexOf : function(record){
9455         return this.data.indexOf(record);
9456     },
9457
9458     /**
9459      * Get the index within the cache of the Record with the passed id.
9460      * @param {String} id The id of the Record to find.
9461      * @return {Number} The index of the Record. Returns -1 if not found.
9462      */
9463     indexOfId : function(id){
9464         return this.data.indexOfKey(id);
9465     },
9466
9467     /**
9468      * Get the Record with the specified id.
9469      * @param {String} id The id of the Record to find.
9470      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9471      */
9472     getById : function(id){
9473         return this.data.key(id);
9474     },
9475
9476     /**
9477      * Get the Record at the specified index.
9478      * @param {Number} index The index of the Record to find.
9479      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9480      */
9481     getAt : function(index){
9482         return this.data.itemAt(index);
9483     },
9484
9485     /**
9486      * Returns a range of Records between specified indices.
9487      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9488      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9489      * @return {Roo.data.Record[]} An array of Records
9490      */
9491     getRange : function(start, end){
9492         return this.data.getRange(start, end);
9493     },
9494
9495     // private
9496     storeOptions : function(o){
9497         o = Roo.apply({}, o);
9498         delete o.callback;
9499         delete o.scope;
9500         this.lastOptions = o;
9501     },
9502
9503     /**
9504      * Loads the Record cache from the configured Proxy using the configured Reader.
9505      * <p>
9506      * If using remote paging, then the first load call must specify the <em>start</em>
9507      * and <em>limit</em> properties in the options.params property to establish the initial
9508      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9509      * <p>
9510      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9511      * and this call will return before the new data has been loaded. Perform any post-processing
9512      * in a callback function, or in a "load" event handler.</strong>
9513      * <p>
9514      * @param {Object} options An object containing properties which control loading options:<ul>
9515      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9516      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9517      * passed the following arguments:<ul>
9518      * <li>r : Roo.data.Record[]</li>
9519      * <li>options: Options object from the load call</li>
9520      * <li>success: Boolean success indicator</li></ul></li>
9521      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9522      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9523      * </ul>
9524      */
9525     load : function(options){
9526         options = options || {};
9527         if(this.fireEvent("beforeload", this, options) !== false){
9528             this.storeOptions(options);
9529             var p = Roo.apply(options.params || {}, this.baseParams);
9530             // if meta was not loaded from remote source.. try requesting it.
9531             if (!this.reader.metaFromRemote) {
9532                 p._requestMeta = 1;
9533             }
9534             if(this.sortInfo && this.remoteSort){
9535                 var pn = this.paramNames;
9536                 p[pn["sort"]] = this.sortInfo.field;
9537                 p[pn["dir"]] = this.sortInfo.direction;
9538             }
9539             if (this.multiSort) {
9540                 var pn = this.paramNames;
9541                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9542             }
9543             
9544             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9545         }
9546     },
9547
9548     /**
9549      * Reloads the Record cache from the configured Proxy using the configured Reader and
9550      * the options from the last load operation performed.
9551      * @param {Object} options (optional) An object containing properties which may override the options
9552      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9553      * the most recently used options are reused).
9554      */
9555     reload : function(options){
9556         this.load(Roo.applyIf(options||{}, this.lastOptions));
9557     },
9558
9559     // private
9560     // Called as a callback by the Reader during a load operation.
9561     loadRecords : function(o, options, success){
9562         if(!o || success === false){
9563             if(success !== false){
9564                 this.fireEvent("load", this, [], options, o);
9565             }
9566             if(options.callback){
9567                 options.callback.call(options.scope || this, [], options, false);
9568             }
9569             return;
9570         }
9571         // if data returned failure - throw an exception.
9572         if (o.success === false) {
9573             // show a message if no listener is registered.
9574             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9575                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9576             }
9577             // loadmask wil be hooked into this..
9578             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9579             return;
9580         }
9581         var r = o.records, t = o.totalRecords || r.length;
9582         
9583         this.fireEvent("beforeloadadd", this, r, options, o);
9584         
9585         if(!options || options.add !== true){
9586             if(this.pruneModifiedRecords){
9587                 this.modified = [];
9588             }
9589             for(var i = 0, len = r.length; i < len; i++){
9590                 r[i].join(this);
9591             }
9592             if(this.snapshot){
9593                 this.data = this.snapshot;
9594                 delete this.snapshot;
9595             }
9596             this.data.clear();
9597             this.data.addAll(r);
9598             this.totalLength = t;
9599             this.applySort();
9600             this.fireEvent("datachanged", this);
9601         }else{
9602             this.totalLength = Math.max(t, this.data.length+r.length);
9603             this.add(r);
9604         }
9605         this.fireEvent("load", this, r, options, o);
9606         if(options.callback){
9607             options.callback.call(options.scope || this, r, options, true);
9608         }
9609     },
9610
9611
9612     /**
9613      * Loads data from a passed data block. A Reader which understands the format of the data
9614      * must have been configured in the constructor.
9615      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9616      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9617      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9618      */
9619     loadData : function(o, append){
9620         var r = this.reader.readRecords(o);
9621         this.loadRecords(r, {add: append}, true);
9622     },
9623
9624     /**
9625      * Gets the number of cached records.
9626      * <p>
9627      * <em>If using paging, this may not be the total size of the dataset. If the data object
9628      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9629      * the data set size</em>
9630      */
9631     getCount : function(){
9632         return this.data.length || 0;
9633     },
9634
9635     /**
9636      * Gets the total number of records in the dataset as returned by the server.
9637      * <p>
9638      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9639      * the dataset size</em>
9640      */
9641     getTotalCount : function(){
9642         return this.totalLength || 0;
9643     },
9644
9645     /**
9646      * Returns the sort state of the Store as an object with two properties:
9647      * <pre><code>
9648  field {String} The name of the field by which the Records are sorted
9649  direction {String} The sort order, "ASC" or "DESC"
9650      * </code></pre>
9651      */
9652     getSortState : function(){
9653         return this.sortInfo;
9654     },
9655
9656     // private
9657     applySort : function(){
9658         if(this.sortInfo && !this.remoteSort){
9659             var s = this.sortInfo, f = s.field;
9660             var st = this.fields.get(f).sortType;
9661             var fn = function(r1, r2){
9662                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9663                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9664             };
9665             this.data.sort(s.direction, fn);
9666             if(this.snapshot && this.snapshot != this.data){
9667                 this.snapshot.sort(s.direction, fn);
9668             }
9669         }
9670     },
9671
9672     /**
9673      * Sets the default sort column and order to be used by the next load operation.
9674      * @param {String} fieldName The name of the field to sort by.
9675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9676      */
9677     setDefaultSort : function(field, dir){
9678         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9679     },
9680
9681     /**
9682      * Sort the Records.
9683      * If remote sorting is used, the sort is performed on the server, and the cache is
9684      * reloaded. If local sorting is used, the cache is sorted internally.
9685      * @param {String} fieldName The name of the field to sort by.
9686      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9687      */
9688     sort : function(fieldName, dir){
9689         var f = this.fields.get(fieldName);
9690         if(!dir){
9691             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9692             
9693             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9694                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9695             }else{
9696                 dir = f.sortDir;
9697             }
9698         }
9699         this.sortToggle[f.name] = dir;
9700         this.sortInfo = {field: f.name, direction: dir};
9701         if(!this.remoteSort){
9702             this.applySort();
9703             this.fireEvent("datachanged", this);
9704         }else{
9705             this.load(this.lastOptions);
9706         }
9707     },
9708
9709     /**
9710      * Calls the specified function for each of the Records in the cache.
9711      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9712      * Returning <em>false</em> aborts and exits the iteration.
9713      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9714      */
9715     each : function(fn, scope){
9716         this.data.each(fn, scope);
9717     },
9718
9719     /**
9720      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9721      * (e.g., during paging).
9722      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9723      */
9724     getModifiedRecords : function(){
9725         return this.modified;
9726     },
9727
9728     // private
9729     createFilterFn : function(property, value, anyMatch){
9730         if(!value.exec){ // not a regex
9731             value = String(value);
9732             if(value.length == 0){
9733                 return false;
9734             }
9735             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9736         }
9737         return function(r){
9738             return value.test(r.data[property]);
9739         };
9740     },
9741
9742     /**
9743      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9744      * @param {String} property A field on your records
9745      * @param {Number} start The record index to start at (defaults to 0)
9746      * @param {Number} end The last record index to include (defaults to length - 1)
9747      * @return {Number} The sum
9748      */
9749     sum : function(property, start, end){
9750         var rs = this.data.items, v = 0;
9751         start = start || 0;
9752         end = (end || end === 0) ? end : rs.length-1;
9753
9754         for(var i = start; i <= end; i++){
9755             v += (rs[i].data[property] || 0);
9756         }
9757         return v;
9758     },
9759
9760     /**
9761      * Filter the records by a specified property.
9762      * @param {String} field A field on your records
9763      * @param {String/RegExp} value Either a string that the field
9764      * should start with or a RegExp to test against the field
9765      * @param {Boolean} anyMatch True to match any part not just the beginning
9766      */
9767     filter : function(property, value, anyMatch){
9768         var fn = this.createFilterFn(property, value, anyMatch);
9769         return fn ? this.filterBy(fn) : this.clearFilter();
9770     },
9771
9772     /**
9773      * Filter by a function. The specified function will be called with each
9774      * record in this data source. If the function returns true the record is included,
9775      * otherwise it is filtered.
9776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9777      * @param {Object} scope (optional) The scope of the function (defaults to this)
9778      */
9779     filterBy : function(fn, scope){
9780         this.snapshot = this.snapshot || this.data;
9781         this.data = this.queryBy(fn, scope||this);
9782         this.fireEvent("datachanged", this);
9783     },
9784
9785     /**
9786      * Query the records by a specified property.
9787      * @param {String} field A field on your records
9788      * @param {String/RegExp} value Either a string that the field
9789      * should start with or a RegExp to test against the field
9790      * @param {Boolean} anyMatch True to match any part not just the beginning
9791      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9792      */
9793     query : function(property, value, anyMatch){
9794         var fn = this.createFilterFn(property, value, anyMatch);
9795         return fn ? this.queryBy(fn) : this.data.clone();
9796     },
9797
9798     /**
9799      * Query by a function. The specified function will be called with each
9800      * record in this data source. If the function returns true the record is included
9801      * in the results.
9802      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9803      * @param {Object} scope (optional) The scope of the function (defaults to this)
9804       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9805      **/
9806     queryBy : function(fn, scope){
9807         var data = this.snapshot || this.data;
9808         return data.filterBy(fn, scope||this);
9809     },
9810
9811     /**
9812      * Collects unique values for a particular dataIndex from this store.
9813      * @param {String} dataIndex The property to collect
9814      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9815      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9816      * @return {Array} An array of the unique values
9817      **/
9818     collect : function(dataIndex, allowNull, bypassFilter){
9819         var d = (bypassFilter === true && this.snapshot) ?
9820                 this.snapshot.items : this.data.items;
9821         var v, sv, r = [], l = {};
9822         for(var i = 0, len = d.length; i < len; i++){
9823             v = d[i].data[dataIndex];
9824             sv = String(v);
9825             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9826                 l[sv] = true;
9827                 r[r.length] = v;
9828             }
9829         }
9830         return r;
9831     },
9832
9833     /**
9834      * Revert to a view of the Record cache with no filtering applied.
9835      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9836      */
9837     clearFilter : function(suppressEvent){
9838         if(this.snapshot && this.snapshot != this.data){
9839             this.data = this.snapshot;
9840             delete this.snapshot;
9841             if(suppressEvent !== true){
9842                 this.fireEvent("datachanged", this);
9843             }
9844         }
9845     },
9846
9847     // private
9848     afterEdit : function(record){
9849         if(this.modified.indexOf(record) == -1){
9850             this.modified.push(record);
9851         }
9852         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9853     },
9854     
9855     // private
9856     afterReject : function(record){
9857         this.modified.remove(record);
9858         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9859     },
9860
9861     // private
9862     afterCommit : function(record){
9863         this.modified.remove(record);
9864         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9865     },
9866
9867     /**
9868      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9869      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9870      */
9871     commitChanges : function(){
9872         var m = this.modified.slice(0);
9873         this.modified = [];
9874         for(var i = 0, len = m.length; i < len; i++){
9875             m[i].commit();
9876         }
9877     },
9878
9879     /**
9880      * Cancel outstanding changes on all changed records.
9881      */
9882     rejectChanges : function(){
9883         var m = this.modified.slice(0);
9884         this.modified = [];
9885         for(var i = 0, len = m.length; i < len; i++){
9886             m[i].reject();
9887         }
9888     },
9889
9890     onMetaChange : function(meta, rtype, o){
9891         this.recordType = rtype;
9892         this.fields = rtype.prototype.fields;
9893         delete this.snapshot;
9894         this.sortInfo = meta.sortInfo || this.sortInfo;
9895         this.modified = [];
9896         this.fireEvent('metachange', this, this.reader.meta);
9897     },
9898     
9899     moveIndex : function(data, type)
9900     {
9901         var index = this.indexOf(data);
9902         
9903         var newIndex = index + type;
9904         
9905         this.remove(data);
9906         
9907         this.insert(newIndex, data);
9908         
9909     }
9910 });/*
9911  * Based on:
9912  * Ext JS Library 1.1.1
9913  * Copyright(c) 2006-2007, Ext JS, LLC.
9914  *
9915  * Originally Released Under LGPL - original licence link has changed is not relivant.
9916  *
9917  * Fork - LGPL
9918  * <script type="text/javascript">
9919  */
9920
9921 /**
9922  * @class Roo.data.SimpleStore
9923  * @extends Roo.data.Store
9924  * Small helper class to make creating Stores from Array data easier.
9925  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9926  * @cfg {Array} fields An array of field definition objects, or field name strings.
9927  * @cfg {Array} data The multi-dimensional array of data
9928  * @constructor
9929  * @param {Object} config
9930  */
9931 Roo.data.SimpleStore = function(config){
9932     Roo.data.SimpleStore.superclass.constructor.call(this, {
9933         isLocal : true,
9934         reader: new Roo.data.ArrayReader({
9935                 id: config.id
9936             },
9937             Roo.data.Record.create(config.fields)
9938         ),
9939         proxy : new Roo.data.MemoryProxy(config.data)
9940     });
9941     this.load();
9942 };
9943 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9944  * Based on:
9945  * Ext JS Library 1.1.1
9946  * Copyright(c) 2006-2007, Ext JS, LLC.
9947  *
9948  * Originally Released Under LGPL - original licence link has changed is not relivant.
9949  *
9950  * Fork - LGPL
9951  * <script type="text/javascript">
9952  */
9953
9954 /**
9955 /**
9956  * @extends Roo.data.Store
9957  * @class Roo.data.JsonStore
9958  * Small helper class to make creating Stores for JSON data easier. <br/>
9959 <pre><code>
9960 var store = new Roo.data.JsonStore({
9961     url: 'get-images.php',
9962     root: 'images',
9963     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9964 });
9965 </code></pre>
9966  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9967  * JsonReader and HttpProxy (unless inline data is provided).</b>
9968  * @cfg {Array} fields An array of field definition objects, or field name strings.
9969  * @constructor
9970  * @param {Object} config
9971  */
9972 Roo.data.JsonStore = function(c){
9973     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9974         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9975         reader: new Roo.data.JsonReader(c, c.fields)
9976     }));
9977 };
9978 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9979  * Based on:
9980  * Ext JS Library 1.1.1
9981  * Copyright(c) 2006-2007, Ext JS, LLC.
9982  *
9983  * Originally Released Under LGPL - original licence link has changed is not relivant.
9984  *
9985  * Fork - LGPL
9986  * <script type="text/javascript">
9987  */
9988
9989  
9990 Roo.data.Field = function(config){
9991     if(typeof config == "string"){
9992         config = {name: config};
9993     }
9994     Roo.apply(this, config);
9995     
9996     if(!this.type){
9997         this.type = "auto";
9998     }
9999     
10000     var st = Roo.data.SortTypes;
10001     // named sortTypes are supported, here we look them up
10002     if(typeof this.sortType == "string"){
10003         this.sortType = st[this.sortType];
10004     }
10005     
10006     // set default sortType for strings and dates
10007     if(!this.sortType){
10008         switch(this.type){
10009             case "string":
10010                 this.sortType = st.asUCString;
10011                 break;
10012             case "date":
10013                 this.sortType = st.asDate;
10014                 break;
10015             default:
10016                 this.sortType = st.none;
10017         }
10018     }
10019
10020     // define once
10021     var stripRe = /[\$,%]/g;
10022
10023     // prebuilt conversion function for this field, instead of
10024     // switching every time we're reading a value
10025     if(!this.convert){
10026         var cv, dateFormat = this.dateFormat;
10027         switch(this.type){
10028             case "":
10029             case "auto":
10030             case undefined:
10031                 cv = function(v){ return v; };
10032                 break;
10033             case "string":
10034                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10035                 break;
10036             case "int":
10037                 cv = function(v){
10038                     return v !== undefined && v !== null && v !== '' ?
10039                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10040                     };
10041                 break;
10042             case "float":
10043                 cv = function(v){
10044                     return v !== undefined && v !== null && v !== '' ?
10045                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10046                     };
10047                 break;
10048             case "bool":
10049             case "boolean":
10050                 cv = function(v){ return v === true || v === "true" || v == 1; };
10051                 break;
10052             case "date":
10053                 cv = function(v){
10054                     if(!v){
10055                         return '';
10056                     }
10057                     if(v instanceof Date){
10058                         return v;
10059                     }
10060                     if(dateFormat){
10061                         if(dateFormat == "timestamp"){
10062                             return new Date(v*1000);
10063                         }
10064                         return Date.parseDate(v, dateFormat);
10065                     }
10066                     var parsed = Date.parse(v);
10067                     return parsed ? new Date(parsed) : null;
10068                 };
10069              break;
10070             
10071         }
10072         this.convert = cv;
10073     }
10074 };
10075
10076 Roo.data.Field.prototype = {
10077     dateFormat: null,
10078     defaultValue: "",
10079     mapping: null,
10080     sortType : null,
10081     sortDir : "ASC"
10082 };/*
10083  * Based on:
10084  * Ext JS Library 1.1.1
10085  * Copyright(c) 2006-2007, Ext JS, LLC.
10086  *
10087  * Originally Released Under LGPL - original licence link has changed is not relivant.
10088  *
10089  * Fork - LGPL
10090  * <script type="text/javascript">
10091  */
10092  
10093 // Base class for reading structured data from a data source.  This class is intended to be
10094 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10095
10096 /**
10097  * @class Roo.data.DataReader
10098  * Base class for reading structured data from a data source.  This class is intended to be
10099  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10100  */
10101
10102 Roo.data.DataReader = function(meta, recordType){
10103     
10104     this.meta = meta;
10105     
10106     this.recordType = recordType instanceof Array ? 
10107         Roo.data.Record.create(recordType) : recordType;
10108 };
10109
10110 Roo.data.DataReader.prototype = {
10111      /**
10112      * Create an empty record
10113      * @param {Object} data (optional) - overlay some values
10114      * @return {Roo.data.Record} record created.
10115      */
10116     newRow :  function(d) {
10117         var da =  {};
10118         this.recordType.prototype.fields.each(function(c) {
10119             switch( c.type) {
10120                 case 'int' : da[c.name] = 0; break;
10121                 case 'date' : da[c.name] = new Date(); break;
10122                 case 'float' : da[c.name] = 0.0; break;
10123                 case 'boolean' : da[c.name] = false; break;
10124                 default : da[c.name] = ""; break;
10125             }
10126             
10127         });
10128         return new this.recordType(Roo.apply(da, d));
10129     }
10130     
10131 };/*
10132  * Based on:
10133  * Ext JS Library 1.1.1
10134  * Copyright(c) 2006-2007, Ext JS, LLC.
10135  *
10136  * Originally Released Under LGPL - original licence link has changed is not relivant.
10137  *
10138  * Fork - LGPL
10139  * <script type="text/javascript">
10140  */
10141
10142 /**
10143  * @class Roo.data.DataProxy
10144  * @extends Roo.data.Observable
10145  * This class is an abstract base class for implementations which provide retrieval of
10146  * unformatted data objects.<br>
10147  * <p>
10148  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10149  * (of the appropriate type which knows how to parse the data object) to provide a block of
10150  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10151  * <p>
10152  * Custom implementations must implement the load method as described in
10153  * {@link Roo.data.HttpProxy#load}.
10154  */
10155 Roo.data.DataProxy = function(){
10156     this.addEvents({
10157         /**
10158          * @event beforeload
10159          * Fires before a network request is made to retrieve a data object.
10160          * @param {Object} This DataProxy object.
10161          * @param {Object} params The params parameter to the load function.
10162          */
10163         beforeload : true,
10164         /**
10165          * @event load
10166          * Fires before the load method's callback is called.
10167          * @param {Object} This DataProxy object.
10168          * @param {Object} o The data object.
10169          * @param {Object} arg The callback argument object passed to the load function.
10170          */
10171         load : true,
10172         /**
10173          * @event loadexception
10174          * Fires if an Exception occurs during data retrieval.
10175          * @param {Object} This DataProxy object.
10176          * @param {Object} o The data object.
10177          * @param {Object} arg The callback argument object passed to the load function.
10178          * @param {Object} e The Exception.
10179          */
10180         loadexception : true
10181     });
10182     Roo.data.DataProxy.superclass.constructor.call(this);
10183 };
10184
10185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10186
10187     /**
10188      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10189      */
10190 /*
10191  * Based on:
10192  * Ext JS Library 1.1.1
10193  * Copyright(c) 2006-2007, Ext JS, LLC.
10194  *
10195  * Originally Released Under LGPL - original licence link has changed is not relivant.
10196  *
10197  * Fork - LGPL
10198  * <script type="text/javascript">
10199  */
10200 /**
10201  * @class Roo.data.MemoryProxy
10202  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10203  * to the Reader when its load method is called.
10204  * @constructor
10205  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10206  */
10207 Roo.data.MemoryProxy = function(data){
10208     if (data.data) {
10209         data = data.data;
10210     }
10211     Roo.data.MemoryProxy.superclass.constructor.call(this);
10212     this.data = data;
10213 };
10214
10215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10216     /**
10217      * Load data from the requested source (in this case an in-memory
10218      * data object passed to the constructor), read the data object into
10219      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10220      * process that block using the passed callback.
10221      * @param {Object} params This parameter is not used by the MemoryProxy class.
10222      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10223      * object into a block of Roo.data.Records.
10224      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10225      * The function must be passed <ul>
10226      * <li>The Record block object</li>
10227      * <li>The "arg" argument from the load function</li>
10228      * <li>A boolean success indicator</li>
10229      * </ul>
10230      * @param {Object} scope The scope in which to call the callback
10231      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10232      */
10233     load : function(params, reader, callback, scope, arg){
10234         params = params || {};
10235         var result;
10236         try {
10237             result = reader.readRecords(this.data);
10238         }catch(e){
10239             this.fireEvent("loadexception", this, arg, null, e);
10240             callback.call(scope, null, arg, false);
10241             return;
10242         }
10243         callback.call(scope, result, arg, true);
10244     },
10245     
10246     // private
10247     update : function(params, records){
10248         
10249     }
10250 });/*
10251  * Based on:
10252  * Ext JS Library 1.1.1
10253  * Copyright(c) 2006-2007, Ext JS, LLC.
10254  *
10255  * Originally Released Under LGPL - original licence link has changed is not relivant.
10256  *
10257  * Fork - LGPL
10258  * <script type="text/javascript">
10259  */
10260 /**
10261  * @class Roo.data.HttpProxy
10262  * @extends Roo.data.DataProxy
10263  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10264  * configured to reference a certain URL.<br><br>
10265  * <p>
10266  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10267  * from which the running page was served.<br><br>
10268  * <p>
10269  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10270  * <p>
10271  * Be aware that to enable the browser to parse an XML document, the server must set
10272  * the Content-Type header in the HTTP response to "text/xml".
10273  * @constructor
10274  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10275  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10276  * will be used to make the request.
10277  */
10278 Roo.data.HttpProxy = function(conn){
10279     Roo.data.HttpProxy.superclass.constructor.call(this);
10280     // is conn a conn config or a real conn?
10281     this.conn = conn;
10282     this.useAjax = !conn || !conn.events;
10283   
10284 };
10285
10286 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10287     // thse are take from connection...
10288     
10289     /**
10290      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10291      */
10292     /**
10293      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10294      * extra parameters to each request made by this object. (defaults to undefined)
10295      */
10296     /**
10297      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10298      *  to each request made by this object. (defaults to undefined)
10299      */
10300     /**
10301      * @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)
10302      */
10303     /**
10304      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10305      */
10306      /**
10307      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10308      * @type Boolean
10309      */
10310   
10311
10312     /**
10313      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10314      * @type Boolean
10315      */
10316     /**
10317      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10318      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10319      * a finer-grained basis than the DataProxy events.
10320      */
10321     getConnection : function(){
10322         return this.useAjax ? Roo.Ajax : this.conn;
10323     },
10324
10325     /**
10326      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10327      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10328      * process that block using the passed callback.
10329      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10330      * for the request to the remote server.
10331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10332      * object into a block of Roo.data.Records.
10333      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10334      * The function must be passed <ul>
10335      * <li>The Record block object</li>
10336      * <li>The "arg" argument from the load function</li>
10337      * <li>A boolean success indicator</li>
10338      * </ul>
10339      * @param {Object} scope The scope in which to call the callback
10340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10341      */
10342     load : function(params, reader, callback, scope, arg){
10343         if(this.fireEvent("beforeload", this, params) !== false){
10344             var  o = {
10345                 params : params || {},
10346                 request: {
10347                     callback : callback,
10348                     scope : scope,
10349                     arg : arg
10350                 },
10351                 reader: reader,
10352                 callback : this.loadResponse,
10353                 scope: this
10354             };
10355             if(this.useAjax){
10356                 Roo.applyIf(o, this.conn);
10357                 if(this.activeRequest){
10358                     Roo.Ajax.abort(this.activeRequest);
10359                 }
10360                 this.activeRequest = Roo.Ajax.request(o);
10361             }else{
10362                 this.conn.request(o);
10363             }
10364         }else{
10365             callback.call(scope||this, null, arg, false);
10366         }
10367     },
10368
10369     // private
10370     loadResponse : function(o, success, response){
10371         delete this.activeRequest;
10372         if(!success){
10373             this.fireEvent("loadexception", this, o, response);
10374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10375             return;
10376         }
10377         var result;
10378         try {
10379             result = o.reader.read(response);
10380         }catch(e){
10381             this.fireEvent("loadexception", this, o, response, e);
10382             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10383             return;
10384         }
10385         
10386         this.fireEvent("load", this, o, o.request.arg);
10387         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10388     },
10389
10390     // private
10391     update : function(dataSet){
10392
10393     },
10394
10395     // private
10396     updateResponse : function(dataSet){
10397
10398     }
10399 });/*
10400  * Based on:
10401  * Ext JS Library 1.1.1
10402  * Copyright(c) 2006-2007, Ext JS, LLC.
10403  *
10404  * Originally Released Under LGPL - original licence link has changed is not relivant.
10405  *
10406  * Fork - LGPL
10407  * <script type="text/javascript">
10408  */
10409
10410 /**
10411  * @class Roo.data.ScriptTagProxy
10412  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10413  * other than the originating domain of the running page.<br><br>
10414  * <p>
10415  * <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
10416  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10417  * <p>
10418  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10419  * source code that is used as the source inside a &lt;script> tag.<br><br>
10420  * <p>
10421  * In order for the browser to process the returned data, the server must wrap the data object
10422  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10423  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10424  * depending on whether the callback name was passed:
10425  * <p>
10426  * <pre><code>
10427 boolean scriptTag = false;
10428 String cb = request.getParameter("callback");
10429 if (cb != null) {
10430     scriptTag = true;
10431     response.setContentType("text/javascript");
10432 } else {
10433     response.setContentType("application/x-json");
10434 }
10435 Writer out = response.getWriter();
10436 if (scriptTag) {
10437     out.write(cb + "(");
10438 }
10439 out.print(dataBlock.toJsonString());
10440 if (scriptTag) {
10441     out.write(");");
10442 }
10443 </pre></code>
10444  *
10445  * @constructor
10446  * @param {Object} config A configuration object.
10447  */
10448 Roo.data.ScriptTagProxy = function(config){
10449     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10450     Roo.apply(this, config);
10451     this.head = document.getElementsByTagName("head")[0];
10452 };
10453
10454 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10455
10456 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10457     /**
10458      * @cfg {String} url The URL from which to request the data object.
10459      */
10460     /**
10461      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10462      */
10463     timeout : 30000,
10464     /**
10465      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10466      * the server the name of the callback function set up by the load call to process the returned data object.
10467      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10468      * javascript output which calls this named function passing the data object as its only parameter.
10469      */
10470     callbackParam : "callback",
10471     /**
10472      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10473      * name to the request.
10474      */
10475     nocache : true,
10476
10477     /**
10478      * Load data from the configured URL, read the data object into
10479      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10480      * process that block using the passed callback.
10481      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10482      * for the request to the remote server.
10483      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10484      * object into a block of Roo.data.Records.
10485      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10486      * The function must be passed <ul>
10487      * <li>The Record block object</li>
10488      * <li>The "arg" argument from the load function</li>
10489      * <li>A boolean success indicator</li>
10490      * </ul>
10491      * @param {Object} scope The scope in which to call the callback
10492      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10493      */
10494     load : function(params, reader, callback, scope, arg){
10495         if(this.fireEvent("beforeload", this, params) !== false){
10496
10497             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10498
10499             var url = this.url;
10500             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10501             if(this.nocache){
10502                 url += "&_dc=" + (new Date().getTime());
10503             }
10504             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10505             var trans = {
10506                 id : transId,
10507                 cb : "stcCallback"+transId,
10508                 scriptId : "stcScript"+transId,
10509                 params : params,
10510                 arg : arg,
10511                 url : url,
10512                 callback : callback,
10513                 scope : scope,
10514                 reader : reader
10515             };
10516             var conn = this;
10517
10518             window[trans.cb] = function(o){
10519                 conn.handleResponse(o, trans);
10520             };
10521
10522             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10523
10524             if(this.autoAbort !== false){
10525                 this.abort();
10526             }
10527
10528             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10529
10530             var script = document.createElement("script");
10531             script.setAttribute("src", url);
10532             script.setAttribute("type", "text/javascript");
10533             script.setAttribute("id", trans.scriptId);
10534             this.head.appendChild(script);
10535
10536             this.trans = trans;
10537         }else{
10538             callback.call(scope||this, null, arg, false);
10539         }
10540     },
10541
10542     // private
10543     isLoading : function(){
10544         return this.trans ? true : false;
10545     },
10546
10547     /**
10548      * Abort the current server request.
10549      */
10550     abort : function(){
10551         if(this.isLoading()){
10552             this.destroyTrans(this.trans);
10553         }
10554     },
10555
10556     // private
10557     destroyTrans : function(trans, isLoaded){
10558         this.head.removeChild(document.getElementById(trans.scriptId));
10559         clearTimeout(trans.timeoutId);
10560         if(isLoaded){
10561             window[trans.cb] = undefined;
10562             try{
10563                 delete window[trans.cb];
10564             }catch(e){}
10565         }else{
10566             // if hasn't been loaded, wait for load to remove it to prevent script error
10567             window[trans.cb] = function(){
10568                 window[trans.cb] = undefined;
10569                 try{
10570                     delete window[trans.cb];
10571                 }catch(e){}
10572             };
10573         }
10574     },
10575
10576     // private
10577     handleResponse : function(o, trans){
10578         this.trans = false;
10579         this.destroyTrans(trans, true);
10580         var result;
10581         try {
10582             result = trans.reader.readRecords(o);
10583         }catch(e){
10584             this.fireEvent("loadexception", this, o, trans.arg, e);
10585             trans.callback.call(trans.scope||window, null, trans.arg, false);
10586             return;
10587         }
10588         this.fireEvent("load", this, o, trans.arg);
10589         trans.callback.call(trans.scope||window, result, trans.arg, true);
10590     },
10591
10592     // private
10593     handleFailure : function(trans){
10594         this.trans = false;
10595         this.destroyTrans(trans, false);
10596         this.fireEvent("loadexception", this, null, trans.arg);
10597         trans.callback.call(trans.scope||window, null, trans.arg, false);
10598     }
10599 });/*
10600  * Based on:
10601  * Ext JS Library 1.1.1
10602  * Copyright(c) 2006-2007, Ext JS, LLC.
10603  *
10604  * Originally Released Under LGPL - original licence link has changed is not relivant.
10605  *
10606  * Fork - LGPL
10607  * <script type="text/javascript">
10608  */
10609
10610 /**
10611  * @class Roo.data.JsonReader
10612  * @extends Roo.data.DataReader
10613  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10614  * based on mappings in a provided Roo.data.Record constructor.
10615  * 
10616  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10617  * in the reply previously. 
10618  * 
10619  * <p>
10620  * Example code:
10621  * <pre><code>
10622 var RecordDef = Roo.data.Record.create([
10623     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10624     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10625 ]);
10626 var myReader = new Roo.data.JsonReader({
10627     totalProperty: "results",    // The property which contains the total dataset size (optional)
10628     root: "rows",                // The property which contains an Array of row objects
10629     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10630 }, RecordDef);
10631 </code></pre>
10632  * <p>
10633  * This would consume a JSON file like this:
10634  * <pre><code>
10635 { 'results': 2, 'rows': [
10636     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10637     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10638 }
10639 </code></pre>
10640  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10641  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10642  * paged from the remote server.
10643  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10644  * @cfg {String} root name of the property which contains the Array of row objects.
10645  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10646  * @constructor
10647  * Create a new JsonReader
10648  * @param {Object} meta Metadata configuration options
10649  * @param {Object} recordType Either an Array of field definition objects,
10650  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10651  */
10652 Roo.data.JsonReader = function(meta, recordType){
10653     
10654     meta = meta || {};
10655     // set some defaults:
10656     Roo.applyIf(meta, {
10657         totalProperty: 'total',
10658         successProperty : 'success',
10659         root : 'data',
10660         id : 'id'
10661     });
10662     
10663     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10664 };
10665 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10666     
10667     /**
10668      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10669      * Used by Store query builder to append _requestMeta to params.
10670      * 
10671      */
10672     metaFromRemote : false,
10673     /**
10674      * This method is only used by a DataProxy which has retrieved data from a remote server.
10675      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10676      * @return {Object} data A data block which is used by an Roo.data.Store object as
10677      * a cache of Roo.data.Records.
10678      */
10679     read : function(response){
10680         var json = response.responseText;
10681        
10682         var o = /* eval:var:o */ eval("("+json+")");
10683         if(!o) {
10684             throw {message: "JsonReader.read: Json object not found"};
10685         }
10686         
10687         if(o.metaData){
10688             
10689             delete this.ef;
10690             this.metaFromRemote = true;
10691             this.meta = o.metaData;
10692             this.recordType = Roo.data.Record.create(o.metaData.fields);
10693             this.onMetaChange(this.meta, this.recordType, o);
10694         }
10695         return this.readRecords(o);
10696     },
10697
10698     // private function a store will implement
10699     onMetaChange : function(meta, recordType, o){
10700
10701     },
10702
10703     /**
10704          * @ignore
10705          */
10706     simpleAccess: function(obj, subsc) {
10707         return obj[subsc];
10708     },
10709
10710         /**
10711          * @ignore
10712          */
10713     getJsonAccessor: function(){
10714         var re = /[\[\.]/;
10715         return function(expr) {
10716             try {
10717                 return(re.test(expr))
10718                     ? new Function("obj", "return obj." + expr)
10719                     : function(obj){
10720                         return obj[expr];
10721                     };
10722             } catch(e){}
10723             return Roo.emptyFn;
10724         };
10725     }(),
10726
10727     /**
10728      * Create a data block containing Roo.data.Records from an XML document.
10729      * @param {Object} o An object which contains an Array of row objects in the property specified
10730      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10731      * which contains the total size of the dataset.
10732      * @return {Object} data A data block which is used by an Roo.data.Store object as
10733      * a cache of Roo.data.Records.
10734      */
10735     readRecords : function(o){
10736         /**
10737          * After any data loads, the raw JSON data is available for further custom processing.
10738          * @type Object
10739          */
10740         this.o = o;
10741         var s = this.meta, Record = this.recordType,
10742             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10743
10744 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10745         if (!this.ef) {
10746             if(s.totalProperty) {
10747                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10748                 }
10749                 if(s.successProperty) {
10750                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10751                 }
10752                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10753                 if (s.id) {
10754                         var g = this.getJsonAccessor(s.id);
10755                         this.getId = function(rec) {
10756                                 var r = g(rec);  
10757                                 return (r === undefined || r === "") ? null : r;
10758                         };
10759                 } else {
10760                         this.getId = function(){return null;};
10761                 }
10762             this.ef = [];
10763             for(var jj = 0; jj < fl; jj++){
10764                 f = fi[jj];
10765                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10766                 this.ef[jj] = this.getJsonAccessor(map);
10767             }
10768         }
10769
10770         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10771         if(s.totalProperty){
10772             var vt = parseInt(this.getTotal(o), 10);
10773             if(!isNaN(vt)){
10774                 totalRecords = vt;
10775             }
10776         }
10777         if(s.successProperty){
10778             var vs = this.getSuccess(o);
10779             if(vs === false || vs === 'false'){
10780                 success = false;
10781             }
10782         }
10783         var records = [];
10784         for(var i = 0; i < c; i++){
10785                 var n = root[i];
10786             var values = {};
10787             var id = this.getId(n);
10788             for(var j = 0; j < fl; j++){
10789                 f = fi[j];
10790             var v = this.ef[j](n);
10791             if (!f.convert) {
10792                 Roo.log('missing convert for ' + f.name);
10793                 Roo.log(f);
10794                 continue;
10795             }
10796             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10797             }
10798             var record = new Record(values, id);
10799             record.json = n;
10800             records[i] = record;
10801         }
10802         return {
10803             raw : o,
10804             success : success,
10805             records : records,
10806             totalRecords : totalRecords
10807         };
10808     }
10809 });/*
10810  * Based on:
10811  * Ext JS Library 1.1.1
10812  * Copyright(c) 2006-2007, Ext JS, LLC.
10813  *
10814  * Originally Released Under LGPL - original licence link has changed is not relivant.
10815  *
10816  * Fork - LGPL
10817  * <script type="text/javascript">
10818  */
10819
10820 /**
10821  * @class Roo.data.ArrayReader
10822  * @extends Roo.data.DataReader
10823  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10824  * Each element of that Array represents a row of data fields. The
10825  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10826  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10827  * <p>
10828  * Example code:.
10829  * <pre><code>
10830 var RecordDef = Roo.data.Record.create([
10831     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10832     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10833 ]);
10834 var myReader = new Roo.data.ArrayReader({
10835     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10836 }, RecordDef);
10837 </code></pre>
10838  * <p>
10839  * This would consume an Array like this:
10840  * <pre><code>
10841 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10842   </code></pre>
10843  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10844  * @constructor
10845  * Create a new JsonReader
10846  * @param {Object} meta Metadata configuration options.
10847  * @param {Object} recordType Either an Array of field definition objects
10848  * as specified to {@link Roo.data.Record#create},
10849  * or an {@link Roo.data.Record} object
10850  * created using {@link Roo.data.Record#create}.
10851  */
10852 Roo.data.ArrayReader = function(meta, recordType){
10853     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10854 };
10855
10856 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10857     /**
10858      * Create a data block containing Roo.data.Records from an XML document.
10859      * @param {Object} o An Array of row objects which represents the dataset.
10860      * @return {Object} data A data block which is used by an Roo.data.Store object as
10861      * a cache of Roo.data.Records.
10862      */
10863     readRecords : function(o){
10864         var sid = this.meta ? this.meta.id : null;
10865         var recordType = this.recordType, fields = recordType.prototype.fields;
10866         var records = [];
10867         var root = o;
10868             for(var i = 0; i < root.length; i++){
10869                     var n = root[i];
10870                 var values = {};
10871                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10872                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10873                 var f = fields.items[j];
10874                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10875                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10876                 v = f.convert(v);
10877                 values[f.name] = v;
10878             }
10879                 var record = new recordType(values, id);
10880                 record.json = n;
10881                 records[records.length] = record;
10882             }
10883             return {
10884                 records : records,
10885                 totalRecords : records.length
10886             };
10887     }
10888 });/*
10889  * - LGPL
10890  * * 
10891  */
10892
10893 /**
10894  * @class Roo.bootstrap.ComboBox
10895  * @extends Roo.bootstrap.TriggerField
10896  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10897  * @cfg {Boolean} append (true|false) default false
10898  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10899  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10900  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10901  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10902  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10903  * @constructor
10904  * Create a new ComboBox.
10905  * @param {Object} config Configuration options
10906  */
10907 Roo.bootstrap.ComboBox = function(config){
10908     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10909     this.addEvents({
10910         /**
10911          * @event expand
10912          * Fires when the dropdown list is expanded
10913              * @param {Roo.bootstrap.ComboBox} combo This combo box
10914              */
10915         'expand' : true,
10916         /**
10917          * @event collapse
10918          * Fires when the dropdown list is collapsed
10919              * @param {Roo.bootstrap.ComboBox} combo This combo box
10920              */
10921         'collapse' : true,
10922         /**
10923          * @event beforeselect
10924          * Fires before a list item is selected. Return false to cancel the selection.
10925              * @param {Roo.bootstrap.ComboBox} combo This combo box
10926              * @param {Roo.data.Record} record The data record returned from the underlying store
10927              * @param {Number} index The index of the selected item in the dropdown list
10928              */
10929         'beforeselect' : true,
10930         /**
10931          * @event select
10932          * Fires when a list item is selected
10933              * @param {Roo.bootstrap.ComboBox} combo This combo box
10934              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10935              * @param {Number} index The index of the selected item in the dropdown list
10936              */
10937         'select' : true,
10938         /**
10939          * @event beforequery
10940          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10941          * The event object passed has these properties:
10942              * @param {Roo.bootstrap.ComboBox} combo This combo box
10943              * @param {String} query The query
10944              * @param {Boolean} forceAll true to force "all" query
10945              * @param {Boolean} cancel true to cancel the query
10946              * @param {Object} e The query event object
10947              */
10948         'beforequery': true,
10949          /**
10950          * @event add
10951          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10952              * @param {Roo.bootstrap.ComboBox} combo This combo box
10953              */
10954         'add' : true,
10955         /**
10956          * @event edit
10957          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10958              * @param {Roo.bootstrap.ComboBox} combo This combo box
10959              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10960              */
10961         'edit' : true,
10962         /**
10963          * @event remove
10964          * Fires when the remove value from the combobox array
10965              * @param {Roo.bootstrap.ComboBox} combo This combo box
10966              */
10967         'remove' : true,
10968         /**
10969          * @event specialfilter
10970          * Fires when specialfilter
10971             * @param {Roo.bootstrap.ComboBox} combo This combo box
10972             */
10973         'specialfilter' : true
10974         
10975     });
10976     
10977     this.item = [];
10978     this.tickItems = [];
10979     
10980     this.selectedIndex = -1;
10981     if(this.mode == 'local'){
10982         if(config.queryDelay === undefined){
10983             this.queryDelay = 10;
10984         }
10985         if(config.minChars === undefined){
10986             this.minChars = 0;
10987         }
10988     }
10989 };
10990
10991 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10992      
10993     /**
10994      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10995      * rendering into an Roo.Editor, defaults to false)
10996      */
10997     /**
10998      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10999      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11000      */
11001     /**
11002      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11003      */
11004     /**
11005      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11006      * the dropdown list (defaults to undefined, with no header element)
11007      */
11008
11009      /**
11010      * @cfg {String/Roo.Template} tpl The template to use to render the output
11011      */
11012      
11013      /**
11014      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11015      */
11016     listWidth: undefined,
11017     /**
11018      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11019      * mode = 'remote' or 'text' if mode = 'local')
11020      */
11021     displayField: undefined,
11022     
11023     /**
11024      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11025      * mode = 'remote' or 'value' if mode = 'local'). 
11026      * Note: use of a valueField requires the user make a selection
11027      * in order for a value to be mapped.
11028      */
11029     valueField: undefined,
11030     
11031     
11032     /**
11033      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11034      * field's data value (defaults to the underlying DOM element's name)
11035      */
11036     hiddenName: undefined,
11037     /**
11038      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11039      */
11040     listClass: '',
11041     /**
11042      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11043      */
11044     selectedClass: 'active',
11045     
11046     /**
11047      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11048      */
11049     shadow:'sides',
11050     /**
11051      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11052      * anchor positions (defaults to 'tl-bl')
11053      */
11054     listAlign: 'tl-bl?',
11055     /**
11056      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11057      */
11058     maxHeight: 300,
11059     /**
11060      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11061      * query specified by the allQuery config option (defaults to 'query')
11062      */
11063     triggerAction: 'query',
11064     /**
11065      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11066      * (defaults to 4, does not apply if editable = false)
11067      */
11068     minChars : 4,
11069     /**
11070      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11071      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11072      */
11073     typeAhead: false,
11074     /**
11075      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11076      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11077      */
11078     queryDelay: 500,
11079     /**
11080      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11081      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11082      */
11083     pageSize: 0,
11084     /**
11085      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11086      * when editable = true (defaults to false)
11087      */
11088     selectOnFocus:false,
11089     /**
11090      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11091      */
11092     queryParam: 'query',
11093     /**
11094      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11095      * when mode = 'remote' (defaults to 'Loading...')
11096      */
11097     loadingText: 'Loading...',
11098     /**
11099      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11100      */
11101     resizable: false,
11102     /**
11103      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11104      */
11105     handleHeight : 8,
11106     /**
11107      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11108      * traditional select (defaults to true)
11109      */
11110     editable: true,
11111     /**
11112      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11113      */
11114     allQuery: '',
11115     /**
11116      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11117      */
11118     mode: 'remote',
11119     /**
11120      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11121      * listWidth has a higher value)
11122      */
11123     minListWidth : 70,
11124     /**
11125      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11126      * allow the user to set arbitrary text into the field (defaults to false)
11127      */
11128     forceSelection:false,
11129     /**
11130      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11131      * if typeAhead = true (defaults to 250)
11132      */
11133     typeAheadDelay : 250,
11134     /**
11135      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11136      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11137      */
11138     valueNotFoundText : undefined,
11139     /**
11140      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11141      */
11142     blockFocus : false,
11143     
11144     /**
11145      * @cfg {Boolean} disableClear Disable showing of clear button.
11146      */
11147     disableClear : false,
11148     /**
11149      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11150      */
11151     alwaysQuery : false,
11152     
11153     /**
11154      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11155      */
11156     multiple : false,
11157     
11158     /**
11159      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11160      */
11161     invalidClass : "has-warning",
11162     
11163     /**
11164      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11165      */
11166     validClass : "has-success",
11167     
11168     /**
11169      * @cfg {Boolean} specialFilter (true|false) special filter default false
11170      */
11171     specialFilter : false,
11172     
11173     //private
11174     addicon : false,
11175     editicon: false,
11176     
11177     page: 0,
11178     hasQuery: false,
11179     append: false,
11180     loadNext: false,
11181     autoFocus : true,
11182     tickable : false,
11183     btnPosition : 'right',
11184     triggerList : true,
11185     showToggleBtn : true,
11186     // element that contains real text value.. (when hidden is used..)
11187     
11188     getAutoCreate : function()
11189     {
11190         var cfg = false;
11191         
11192         /*
11193          *  Normal ComboBox
11194          */
11195         if(!this.tickable){
11196             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11197             return cfg;
11198         }
11199         
11200         /*
11201          *  ComboBox with tickable selections
11202          */
11203              
11204         var align = this.labelAlign || this.parentLabelAlign();
11205         
11206         cfg = {
11207             cls : 'form-group roo-combobox-tickable' //input-group
11208         };
11209         
11210         var buttons = {
11211             tag : 'div',
11212             cls : 'tickable-buttons',
11213             cn : [
11214                 {
11215                     tag : 'button',
11216                     type : 'button',
11217                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11218                     html : 'Edit'
11219                 },
11220                 {
11221                     tag : 'button',
11222                     type : 'button',
11223                     name : 'ok',
11224                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11225                     html : 'Done'
11226                 },
11227                 {
11228                     tag : 'button',
11229                     type : 'button',
11230                     name : 'cancel',
11231                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11232                     html : 'Cancel'
11233                 }
11234             ]
11235         };
11236         
11237         if(this.editable){
11238             buttons.cn.unshift({
11239                 tag: 'input',
11240                 cls: 'select2-search-field-input'
11241             });
11242         }
11243         
11244         var _this = this;
11245         
11246         Roo.each(buttons.cn, function(c){
11247             if (_this.size) {
11248                 c.cls += ' btn-' + _this.size;
11249             }
11250
11251             if (_this.disabled) {
11252                 c.disabled = true;
11253             }
11254         });
11255         
11256         var box = {
11257             tag: 'div',
11258             cn: [
11259                 {
11260                     tag: 'input',
11261                     type : 'hidden',
11262                     cls: 'form-hidden-field'
11263                 },
11264                 {
11265                     tag: 'ul',
11266                     cls: 'select2-choices',
11267                     cn:[
11268                         {
11269                             tag: 'li',
11270                             cls: 'select2-search-field',
11271                             cn: [
11272
11273                                 buttons
11274                             ]
11275                         }
11276                     ]
11277                 }
11278             ]
11279         }
11280         
11281         var combobox = {
11282             cls: 'select2-container input-group select2-container-multi',
11283             cn: [
11284                 box
11285 //                {
11286 //                    tag: 'ul',
11287 //                    cls: 'typeahead typeahead-long dropdown-menu',
11288 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11289 //                }
11290             ]
11291         };
11292         
11293         if(this.hasFeedback && !this.allowBlank){
11294             
11295             var feedback = {
11296                 tag: 'span',
11297                 cls: 'glyphicon form-control-feedback'
11298             };
11299
11300             combobox.cn.push(feedback);
11301         }
11302         
11303         if (align ==='left' && this.fieldLabel.length) {
11304             
11305                 Roo.log("left and has label");
11306                 cfg.cn = [
11307                     
11308                     {
11309                         tag: 'label',
11310                         'for' :  id,
11311                         cls : 'control-label col-sm-' + this.labelWidth,
11312                         html : this.fieldLabel
11313                         
11314                     },
11315                     {
11316                         cls : "col-sm-" + (12 - this.labelWidth), 
11317                         cn: [
11318                             combobox
11319                         ]
11320                     }
11321                     
11322                 ];
11323         } else if ( this.fieldLabel.length) {
11324                 Roo.log(" label");
11325                  cfg.cn = [
11326                    
11327                     {
11328                         tag: 'label',
11329                         //cls : 'input-group-addon',
11330                         html : this.fieldLabel
11331                         
11332                     },
11333                     
11334                     combobox
11335                     
11336                 ];
11337
11338         } else {
11339             
11340                 Roo.log(" no label && no align");
11341                 cfg = combobox
11342                      
11343                 
11344         }
11345          
11346         var settings=this;
11347         ['xs','sm','md','lg'].map(function(size){
11348             if (settings[size]) {
11349                 cfg.cls += ' col-' + size + '-' + settings[size];
11350             }
11351         });
11352         
11353         return cfg;
11354         
11355     },
11356     
11357     // private
11358     initEvents: function()
11359     {
11360         
11361         if (!this.store) {
11362             throw "can not find store for combo";
11363         }
11364         this.store = Roo.factory(this.store, Roo.data);
11365         
11366         if(this.tickable){
11367             this.initTickableEvents();
11368             return;
11369         }
11370         
11371         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11372         
11373         if(this.hiddenName){
11374             
11375             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11376             
11377             this.hiddenField.dom.value =
11378                 this.hiddenValue !== undefined ? this.hiddenValue :
11379                 this.value !== undefined ? this.value : '';
11380
11381             // prevent input submission
11382             this.el.dom.removeAttribute('name');
11383             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11384              
11385              
11386         }
11387         //if(Roo.isGecko){
11388         //    this.el.dom.setAttribute('autocomplete', 'off');
11389         //}
11390         
11391         var cls = 'x-combo-list';
11392         
11393         //this.list = new Roo.Layer({
11394         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11395         //});
11396         
11397         var _this = this;
11398         
11399         (function(){
11400             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11401             _this.list.setWidth(lw);
11402         }).defer(100);
11403         
11404         this.list.on('mouseover', this.onViewOver, this);
11405         this.list.on('mousemove', this.onViewMove, this);
11406         
11407         this.list.on('scroll', this.onViewScroll, this);
11408         
11409         /*
11410         this.list.swallowEvent('mousewheel');
11411         this.assetHeight = 0;
11412
11413         if(this.title){
11414             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11415             this.assetHeight += this.header.getHeight();
11416         }
11417
11418         this.innerList = this.list.createChild({cls:cls+'-inner'});
11419         this.innerList.on('mouseover', this.onViewOver, this);
11420         this.innerList.on('mousemove', this.onViewMove, this);
11421         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11422         
11423         if(this.allowBlank && !this.pageSize && !this.disableClear){
11424             this.footer = this.list.createChild({cls:cls+'-ft'});
11425             this.pageTb = new Roo.Toolbar(this.footer);
11426            
11427         }
11428         if(this.pageSize){
11429             this.footer = this.list.createChild({cls:cls+'-ft'});
11430             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11431                     {pageSize: this.pageSize});
11432             
11433         }
11434         
11435         if (this.pageTb && this.allowBlank && !this.disableClear) {
11436             var _this = this;
11437             this.pageTb.add(new Roo.Toolbar.Fill(), {
11438                 cls: 'x-btn-icon x-btn-clear',
11439                 text: '&#160;',
11440                 handler: function()
11441                 {
11442                     _this.collapse();
11443                     _this.clearValue();
11444                     _this.onSelect(false, -1);
11445                 }
11446             });
11447         }
11448         if (this.footer) {
11449             this.assetHeight += this.footer.getHeight();
11450         }
11451         */
11452             
11453         if(!this.tpl){
11454             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11455         }
11456
11457         this.view = new Roo.View(this.list, this.tpl, {
11458             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11459         });
11460         //this.view.wrapEl.setDisplayed(false);
11461         this.view.on('click', this.onViewClick, this);
11462         
11463         
11464         
11465         this.store.on('beforeload', this.onBeforeLoad, this);
11466         this.store.on('load', this.onLoad, this);
11467         this.store.on('loadexception', this.onLoadException, this);
11468         /*
11469         if(this.resizable){
11470             this.resizer = new Roo.Resizable(this.list,  {
11471                pinned:true, handles:'se'
11472             });
11473             this.resizer.on('resize', function(r, w, h){
11474                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11475                 this.listWidth = w;
11476                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11477                 this.restrictHeight();
11478             }, this);
11479             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11480         }
11481         */
11482         if(!this.editable){
11483             this.editable = true;
11484             this.setEditable(false);
11485         }
11486         
11487         /*
11488         
11489         if (typeof(this.events.add.listeners) != 'undefined') {
11490             
11491             this.addicon = this.wrap.createChild(
11492                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11493        
11494             this.addicon.on('click', function(e) {
11495                 this.fireEvent('add', this);
11496             }, this);
11497         }
11498         if (typeof(this.events.edit.listeners) != 'undefined') {
11499             
11500             this.editicon = this.wrap.createChild(
11501                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11502             if (this.addicon) {
11503                 this.editicon.setStyle('margin-left', '40px');
11504             }
11505             this.editicon.on('click', function(e) {
11506                 
11507                 // we fire even  if inothing is selected..
11508                 this.fireEvent('edit', this, this.lastData );
11509                 
11510             }, this);
11511         }
11512         */
11513         
11514         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11515             "up" : function(e){
11516                 this.inKeyMode = true;
11517                 this.selectPrev();
11518             },
11519
11520             "down" : function(e){
11521                 if(!this.isExpanded()){
11522                     this.onTriggerClick();
11523                 }else{
11524                     this.inKeyMode = true;
11525                     this.selectNext();
11526                 }
11527             },
11528
11529             "enter" : function(e){
11530 //                this.onViewClick();
11531                 //return true;
11532                 this.collapse();
11533                 
11534                 if(this.fireEvent("specialkey", this, e)){
11535                     this.onViewClick(false);
11536                 }
11537                 
11538                 return true;
11539             },
11540
11541             "esc" : function(e){
11542                 this.collapse();
11543             },
11544
11545             "tab" : function(e){
11546                 this.collapse();
11547                 
11548                 if(this.fireEvent("specialkey", this, e)){
11549                     this.onViewClick(false);
11550                 }
11551                 
11552                 return true;
11553             },
11554
11555             scope : this,
11556
11557             doRelay : function(foo, bar, hname){
11558                 if(hname == 'down' || this.scope.isExpanded()){
11559                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11560                 }
11561                 return true;
11562             },
11563
11564             forceKeyDown: true
11565         });
11566         
11567         
11568         this.queryDelay = Math.max(this.queryDelay || 10,
11569                 this.mode == 'local' ? 10 : 250);
11570         
11571         
11572         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11573         
11574         if(this.typeAhead){
11575             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11576         }
11577         if(this.editable !== false){
11578             this.inputEl().on("keyup", this.onKeyUp, this);
11579         }
11580         if(this.forceSelection){
11581             this.inputEl().on('blur', this.doForce, this);
11582         }
11583         
11584         if(this.multiple){
11585             this.choices = this.el.select('ul.select2-choices', true).first();
11586             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11587         }
11588     },
11589     
11590     initTickableEvents: function()
11591     {   
11592         this.createList();
11593         
11594         if(this.hiddenName){
11595             
11596             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11597             
11598             this.hiddenField.dom.value =
11599                 this.hiddenValue !== undefined ? this.hiddenValue :
11600                 this.value !== undefined ? this.value : '';
11601
11602             // prevent input submission
11603             this.el.dom.removeAttribute('name');
11604             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11605              
11606              
11607         }
11608         
11609 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11610         
11611         this.choices = this.el.select('ul.select2-choices', true).first();
11612         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11613         if(this.triggerList){
11614             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11615         }
11616          
11617         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11618         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11619         
11620         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11621         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11622         
11623         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11624         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11625         
11626         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11627         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11628         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11629         
11630         this.okBtn.hide();
11631         this.cancelBtn.hide();
11632         
11633         var _this = this;
11634         
11635         (function(){
11636             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11637             _this.list.setWidth(lw);
11638         }).defer(100);
11639         
11640         this.list.on('mouseover', this.onViewOver, this);
11641         this.list.on('mousemove', this.onViewMove, this);
11642         
11643         this.list.on('scroll', this.onViewScroll, this);
11644         
11645         if(!this.tpl){
11646             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>';
11647         }
11648
11649         this.view = new Roo.View(this.list, this.tpl, {
11650             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11651         });
11652         
11653         //this.view.wrapEl.setDisplayed(false);
11654         this.view.on('click', this.onViewClick, this);
11655         
11656         
11657         
11658         this.store.on('beforeload', this.onBeforeLoad, this);
11659         this.store.on('load', this.onLoad, this);
11660         this.store.on('loadexception', this.onLoadException, this);
11661         
11662         if(this.editable){
11663             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11664                 "up" : function(e){
11665                     this.inKeyMode = true;
11666                     this.selectPrev();
11667                 },
11668
11669                 "down" : function(e){
11670                     this.inKeyMode = true;
11671                     this.selectNext();
11672                 },
11673
11674                 "enter" : function(e){
11675                     if(this.fireEvent("specialkey", this, e)){
11676                         this.onViewClick(false);
11677                     }
11678                     
11679                     return true;
11680                 },
11681
11682                 "esc" : function(e){
11683                     this.onTickableFooterButtonClick(e, false, false);
11684                 },
11685
11686                 "tab" : function(e){
11687                     this.fireEvent("specialkey", this, e);
11688                     
11689                     this.onTickableFooterButtonClick(e, false, false);
11690                     
11691                     return true;
11692                 },
11693
11694                 scope : this,
11695
11696                 doRelay : function(e, fn, key){
11697                     if(this.scope.isExpanded()){
11698                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11699                     }
11700                     return true;
11701                 },
11702
11703                 forceKeyDown: true
11704             });
11705         }
11706         
11707         this.queryDelay = Math.max(this.queryDelay || 10,
11708                 this.mode == 'local' ? 10 : 250);
11709         
11710         
11711         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11712         
11713         if(this.typeAhead){
11714             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11715         }
11716         
11717         if(this.editable !== false){
11718             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11719         }
11720         
11721     },
11722
11723     onDestroy : function(){
11724         if(this.view){
11725             this.view.setStore(null);
11726             this.view.el.removeAllListeners();
11727             this.view.el.remove();
11728             this.view.purgeListeners();
11729         }
11730         if(this.list){
11731             this.list.dom.innerHTML  = '';
11732         }
11733         
11734         if(this.store){
11735             this.store.un('beforeload', this.onBeforeLoad, this);
11736             this.store.un('load', this.onLoad, this);
11737             this.store.un('loadexception', this.onLoadException, this);
11738         }
11739         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11740     },
11741
11742     // private
11743     fireKey : function(e){
11744         if(e.isNavKeyPress() && !this.list.isVisible()){
11745             this.fireEvent("specialkey", this, e);
11746         }
11747     },
11748
11749     // private
11750     onResize: function(w, h){
11751 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11752 //        
11753 //        if(typeof w != 'number'){
11754 //            // we do not handle it!?!?
11755 //            return;
11756 //        }
11757 //        var tw = this.trigger.getWidth();
11758 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11759 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11760 //        var x = w - tw;
11761 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11762 //            
11763 //        //this.trigger.setStyle('left', x+'px');
11764 //        
11765 //        if(this.list && this.listWidth === undefined){
11766 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11767 //            this.list.setWidth(lw);
11768 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11769 //        }
11770         
11771     
11772         
11773     },
11774
11775     /**
11776      * Allow or prevent the user from directly editing the field text.  If false is passed,
11777      * the user will only be able to select from the items defined in the dropdown list.  This method
11778      * is the runtime equivalent of setting the 'editable' config option at config time.
11779      * @param {Boolean} value True to allow the user to directly edit the field text
11780      */
11781     setEditable : function(value){
11782         if(value == this.editable){
11783             return;
11784         }
11785         this.editable = value;
11786         if(!value){
11787             this.inputEl().dom.setAttribute('readOnly', true);
11788             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11789             this.inputEl().addClass('x-combo-noedit');
11790         }else{
11791             this.inputEl().dom.setAttribute('readOnly', false);
11792             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11793             this.inputEl().removeClass('x-combo-noedit');
11794         }
11795     },
11796
11797     // private
11798     
11799     onBeforeLoad : function(combo,opts){
11800         if(!this.hasFocus){
11801             return;
11802         }
11803          if (!opts.add) {
11804             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11805          }
11806         this.restrictHeight();
11807         this.selectedIndex = -1;
11808     },
11809
11810     // private
11811     onLoad : function(){
11812         
11813         this.hasQuery = false;
11814         
11815         if(!this.hasFocus){
11816             return;
11817         }
11818         
11819         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11820             this.loading.hide();
11821         }
11822              
11823         if(this.store.getCount() > 0){
11824             this.expand();
11825             this.restrictHeight();
11826             if(this.lastQuery == this.allQuery){
11827                 if(this.editable && !this.tickable){
11828                     this.inputEl().dom.select();
11829                 }
11830                 
11831                 if(
11832                     !this.selectByValue(this.value, true) &&
11833                     this.autoFocus && 
11834                     (
11835                         !this.store.lastOptions ||
11836                         typeof(this.store.lastOptions.add) == 'undefined' || 
11837                         this.store.lastOptions.add != true
11838                     )
11839                 ){
11840                     this.select(0, true);
11841                 }
11842             }else{
11843                 if(this.autoFocus){
11844                     this.selectNext();
11845                 }
11846                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11847                     this.taTask.delay(this.typeAheadDelay);
11848                 }
11849             }
11850         }else{
11851             this.onEmptyResults();
11852         }
11853         
11854         //this.el.focus();
11855     },
11856     // private
11857     onLoadException : function()
11858     {
11859         this.hasQuery = false;
11860         
11861         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11862             this.loading.hide();
11863         }
11864         
11865         if(this.tickable && this.editable){
11866             return;
11867         }
11868         
11869         this.collapse();
11870         
11871         Roo.log(this.store.reader.jsonData);
11872         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11873             // fixme
11874             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11875         }
11876         
11877         
11878     },
11879     // private
11880     onTypeAhead : function(){
11881         if(this.store.getCount() > 0){
11882             var r = this.store.getAt(0);
11883             var newValue = r.data[this.displayField];
11884             var len = newValue.length;
11885             var selStart = this.getRawValue().length;
11886             
11887             if(selStart != len){
11888                 this.setRawValue(newValue);
11889                 this.selectText(selStart, newValue.length);
11890             }
11891         }
11892     },
11893
11894     // private
11895     onSelect : function(record, index){
11896         
11897         if(this.fireEvent('beforeselect', this, record, index) !== false){
11898         
11899             this.setFromData(index > -1 ? record.data : false);
11900             
11901             this.collapse();
11902             this.fireEvent('select', this, record, index);
11903         }
11904     },
11905
11906     /**
11907      * Returns the currently selected field value or empty string if no value is set.
11908      * @return {String} value The selected value
11909      */
11910     getValue : function(){
11911         
11912         if(this.multiple){
11913             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11914         }
11915         
11916         if(this.valueField){
11917             return typeof this.value != 'undefined' ? this.value : '';
11918         }else{
11919             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11920         }
11921     },
11922
11923     /**
11924      * Clears any text/value currently set in the field
11925      */
11926     clearValue : function(){
11927         if(this.hiddenField){
11928             this.hiddenField.dom.value = '';
11929         }
11930         this.value = '';
11931         this.setRawValue('');
11932         this.lastSelectionText = '';
11933         this.lastData = false;
11934         
11935     },
11936
11937     /**
11938      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11939      * will be displayed in the field.  If the value does not match the data value of an existing item,
11940      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11941      * Otherwise the field will be blank (although the value will still be set).
11942      * @param {String} value The value to match
11943      */
11944     setValue : function(v){
11945         if(this.multiple){
11946             this.syncValue();
11947             return;
11948         }
11949         
11950         var text = v;
11951         if(this.valueField){
11952             var r = this.findRecord(this.valueField, v);
11953             if(r){
11954                 text = r.data[this.displayField];
11955             }else if(this.valueNotFoundText !== undefined){
11956                 text = this.valueNotFoundText;
11957             }
11958         }
11959         this.lastSelectionText = text;
11960         if(this.hiddenField){
11961             this.hiddenField.dom.value = v;
11962         }
11963         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11964         this.value = v;
11965     },
11966     /**
11967      * @property {Object} the last set data for the element
11968      */
11969     
11970     lastData : false,
11971     /**
11972      * Sets the value of the field based on a object which is related to the record format for the store.
11973      * @param {Object} value the value to set as. or false on reset?
11974      */
11975     setFromData : function(o){
11976         
11977         if(this.multiple){
11978             this.addItem(o);
11979             return;
11980         }
11981             
11982         var dv = ''; // display value
11983         var vv = ''; // value value..
11984         this.lastData = o;
11985         if (this.displayField) {
11986             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11987         } else {
11988             // this is an error condition!!!
11989             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11990         }
11991         
11992         if(this.valueField){
11993             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11994         }
11995         
11996         if(this.hiddenField){
11997             this.hiddenField.dom.value = vv;
11998             
11999             this.lastSelectionText = dv;
12000             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12001             this.value = vv;
12002             return;
12003         }
12004         // no hidden field.. - we store the value in 'value', but still display
12005         // display field!!!!
12006         this.lastSelectionText = dv;
12007         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12008         this.value = vv;
12009         
12010         
12011     },
12012     // private
12013     reset : function(){
12014         // overridden so that last data is reset..
12015         
12016         if(this.multiple){
12017             this.clearItem();
12018             return;
12019         }
12020         
12021         this.setValue(this.originalValue);
12022         this.clearInvalid();
12023         this.lastData = false;
12024         if (this.view) {
12025             this.view.clearSelections();
12026         }
12027     },
12028     // private
12029     findRecord : function(prop, value){
12030         var record;
12031         if(this.store.getCount() > 0){
12032             this.store.each(function(r){
12033                 if(r.data[prop] == value){
12034                     record = r;
12035                     return false;
12036                 }
12037                 return true;
12038             });
12039         }
12040         return record;
12041     },
12042     
12043     getName: function()
12044     {
12045         // returns hidden if it's set..
12046         if (!this.rendered) {return ''};
12047         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12048         
12049     },
12050     // private
12051     onViewMove : function(e, t){
12052         this.inKeyMode = false;
12053     },
12054
12055     // private
12056     onViewOver : function(e, t){
12057         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12058             return;
12059         }
12060         var item = this.view.findItemFromChild(t);
12061         
12062         if(item){
12063             var index = this.view.indexOf(item);
12064             this.select(index, false);
12065         }
12066     },
12067
12068     // private
12069     onViewClick : function(view, doFocus, el, e)
12070     {
12071         var index = this.view.getSelectedIndexes()[0];
12072         
12073         var r = this.store.getAt(index);
12074         
12075         if(this.tickable){
12076             
12077             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12078                 return;
12079             }
12080             
12081             var rm = false;
12082             var _this = this;
12083             
12084             Roo.each(this.tickItems, function(v,k){
12085                 
12086                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12087                     _this.tickItems.splice(k, 1);
12088                     
12089                     if(typeof(e) == 'undefined' && view == false){
12090                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12091                     }
12092                     
12093                     rm = true;
12094                     return;
12095                 }
12096             });
12097             
12098             if(rm){
12099                 return;
12100             }
12101             
12102             this.tickItems.push(r.data);
12103             
12104             if(typeof(e) == 'undefined' && view == false){
12105                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12106             }
12107                     
12108             return;
12109         }
12110         
12111         if(r){
12112             this.onSelect(r, index);
12113         }
12114         if(doFocus !== false && !this.blockFocus){
12115             this.inputEl().focus();
12116         }
12117     },
12118
12119     // private
12120     restrictHeight : function(){
12121         //this.innerList.dom.style.height = '';
12122         //var inner = this.innerList.dom;
12123         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12124         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12125         //this.list.beginUpdate();
12126         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12127         this.list.alignTo(this.inputEl(), this.listAlign);
12128         this.list.alignTo(this.inputEl(), this.listAlign);
12129         //this.list.endUpdate();
12130     },
12131
12132     // private
12133     onEmptyResults : function(){
12134         
12135         if(this.tickable && this.editable){
12136             this.restrictHeight();
12137             return;
12138         }
12139         
12140         this.collapse();
12141     },
12142
12143     /**
12144      * Returns true if the dropdown list is expanded, else false.
12145      */
12146     isExpanded : function(){
12147         return this.list.isVisible();
12148     },
12149
12150     /**
12151      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12152      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12153      * @param {String} value The data value of the item to select
12154      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12155      * selected item if it is not currently in view (defaults to true)
12156      * @return {Boolean} True if the value matched an item in the list, else false
12157      */
12158     selectByValue : function(v, scrollIntoView){
12159         if(v !== undefined && v !== null){
12160             var r = this.findRecord(this.valueField || this.displayField, v);
12161             if(r){
12162                 this.select(this.store.indexOf(r), scrollIntoView);
12163                 return true;
12164             }
12165         }
12166         return false;
12167     },
12168
12169     /**
12170      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12171      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12172      * @param {Number} index The zero-based index of the list item to select
12173      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12174      * selected item if it is not currently in view (defaults to true)
12175      */
12176     select : function(index, scrollIntoView){
12177         this.selectedIndex = index;
12178         this.view.select(index);
12179         if(scrollIntoView !== false){
12180             var el = this.view.getNode(index);
12181             /*
12182              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12183              */
12184             if(el){
12185                 this.list.scrollChildIntoView(el, false);
12186             }
12187         }
12188     },
12189
12190     // private
12191     selectNext : function(){
12192         var ct = this.store.getCount();
12193         if(ct > 0){
12194             if(this.selectedIndex == -1){
12195                 this.select(0);
12196             }else if(this.selectedIndex < ct-1){
12197                 this.select(this.selectedIndex+1);
12198             }
12199         }
12200     },
12201
12202     // private
12203     selectPrev : function(){
12204         var ct = this.store.getCount();
12205         if(ct > 0){
12206             if(this.selectedIndex == -1){
12207                 this.select(0);
12208             }else if(this.selectedIndex != 0){
12209                 this.select(this.selectedIndex-1);
12210             }
12211         }
12212     },
12213
12214     // private
12215     onKeyUp : function(e){
12216         if(this.editable !== false && !e.isSpecialKey()){
12217             this.lastKey = e.getKey();
12218             this.dqTask.delay(this.queryDelay);
12219         }
12220     },
12221
12222     // private
12223     validateBlur : function(){
12224         return !this.list || !this.list.isVisible();   
12225     },
12226
12227     // private
12228     initQuery : function(){
12229         
12230         var v = this.getRawValue();
12231         
12232         if(this.tickable && this.editable){
12233             v = this.tickableInputEl().getValue();
12234         }
12235         
12236         this.doQuery(v);
12237     },
12238
12239     // private
12240     doForce : function(){
12241         if(this.inputEl().dom.value.length > 0){
12242             this.inputEl().dom.value =
12243                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12244              
12245         }
12246     },
12247
12248     /**
12249      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12250      * query allowing the query action to be canceled if needed.
12251      * @param {String} query The SQL query to execute
12252      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12253      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12254      * saved in the current store (defaults to false)
12255      */
12256     doQuery : function(q, forceAll){
12257         
12258         if(q === undefined || q === null){
12259             q = '';
12260         }
12261         var qe = {
12262             query: q,
12263             forceAll: forceAll,
12264             combo: this,
12265             cancel:false
12266         };
12267         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12268             return false;
12269         }
12270         q = qe.query;
12271         
12272         forceAll = qe.forceAll;
12273         if(forceAll === true || (q.length >= this.minChars)){
12274             
12275             this.hasQuery = true;
12276             
12277             if(this.lastQuery != q || this.alwaysQuery){
12278                 this.lastQuery = q;
12279                 if(this.mode == 'local'){
12280                     this.selectedIndex = -1;
12281                     if(forceAll){
12282                         this.store.clearFilter();
12283                     }else{
12284                         
12285                         if(this.specialFilter){
12286                             this.fireEvent('specialfilter', this);
12287                             this.onLoad();
12288                             return;
12289                         }
12290                         
12291                         this.store.filter(this.displayField, q);
12292                     }
12293                     
12294                     this.store.fireEvent("datachanged", this.store);
12295                     
12296                     this.onLoad();
12297                     
12298                     
12299                 }else{
12300                     
12301                     this.store.baseParams[this.queryParam] = q;
12302                     
12303                     var options = {params : this.getParams(q)};
12304                     
12305                     if(this.loadNext){
12306                         options.add = true;
12307                         options.params.start = this.page * this.pageSize;
12308                     }
12309                     
12310                     this.store.load(options);
12311                     
12312                     /*
12313                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12314                      *  we should expand the list on onLoad
12315                      *  so command out it
12316                      */
12317 //                    this.expand();
12318                 }
12319             }else{
12320                 this.selectedIndex = -1;
12321                 this.onLoad();   
12322             }
12323         }
12324         
12325         this.loadNext = false;
12326     },
12327     
12328     // private
12329     getParams : function(q){
12330         var p = {};
12331         //p[this.queryParam] = q;
12332         
12333         if(this.pageSize){
12334             p.start = 0;
12335             p.limit = this.pageSize;
12336         }
12337         return p;
12338     },
12339
12340     /**
12341      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12342      */
12343     collapse : function(){
12344         if(!this.isExpanded()){
12345             return;
12346         }
12347         
12348         this.list.hide();
12349         
12350         if(this.tickable){
12351             this.hasFocus = false;
12352             this.okBtn.hide();
12353             this.cancelBtn.hide();
12354             this.trigger.show();
12355             
12356             if(this.editable){
12357                 this.tickableInputEl().dom.value = '';
12358                 this.tickableInputEl().blur();
12359             }
12360             
12361         }
12362         
12363         Roo.get(document).un('mousedown', this.collapseIf, this);
12364         Roo.get(document).un('mousewheel', this.collapseIf, this);
12365         if (!this.editable) {
12366             Roo.get(document).un('keydown', this.listKeyPress, this);
12367         }
12368         this.fireEvent('collapse', this);
12369     },
12370
12371     // private
12372     collapseIf : function(e){
12373         var in_combo  = e.within(this.el);
12374         var in_list =  e.within(this.list);
12375         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12376         
12377         if (in_combo || in_list || is_list) {
12378             //e.stopPropagation();
12379             return;
12380         }
12381         
12382         if(this.tickable){
12383             this.onTickableFooterButtonClick(e, false, false);
12384         }
12385
12386         this.collapse();
12387         
12388     },
12389
12390     /**
12391      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12392      */
12393     expand : function(){
12394        
12395         if(this.isExpanded() || !this.hasFocus){
12396             return;
12397         }
12398         
12399         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12400         this.list.setWidth(lw);
12401         
12402         
12403          Roo.log('expand');
12404         
12405         this.list.show();
12406         
12407         this.restrictHeight();
12408         
12409         if(this.tickable){
12410             
12411             this.tickItems = Roo.apply([], this.item);
12412             
12413             this.okBtn.show();
12414             this.cancelBtn.show();
12415             this.trigger.hide();
12416             
12417             if(this.editable){
12418                 this.tickableInputEl().focus();
12419             }
12420             
12421         }
12422         
12423         Roo.get(document).on('mousedown', this.collapseIf, this);
12424         Roo.get(document).on('mousewheel', this.collapseIf, this);
12425         if (!this.editable) {
12426             Roo.get(document).on('keydown', this.listKeyPress, this);
12427         }
12428         
12429         this.fireEvent('expand', this);
12430     },
12431
12432     // private
12433     // Implements the default empty TriggerField.onTriggerClick function
12434     onTriggerClick : function(e)
12435     {
12436         Roo.log('trigger click');
12437         
12438         if(this.disabled || !this.triggerList){
12439             return;
12440         }
12441         
12442         this.page = 0;
12443         this.loadNext = false;
12444         
12445         if(this.isExpanded()){
12446             this.collapse();
12447             if (!this.blockFocus) {
12448                 this.inputEl().focus();
12449             }
12450             
12451         }else {
12452             this.hasFocus = true;
12453             if(this.triggerAction == 'all') {
12454                 this.doQuery(this.allQuery, true);
12455             } else {
12456                 this.doQuery(this.getRawValue());
12457             }
12458             if (!this.blockFocus) {
12459                 this.inputEl().focus();
12460             }
12461         }
12462     },
12463     
12464     onTickableTriggerClick : function(e)
12465     {
12466         if(this.disabled){
12467             return;
12468         }
12469         
12470         this.page = 0;
12471         this.loadNext = false;
12472         this.hasFocus = true;
12473         
12474         if(this.triggerAction == 'all') {
12475             this.doQuery(this.allQuery, true);
12476         } else {
12477             this.doQuery(this.getRawValue());
12478         }
12479     },
12480     
12481     onSearchFieldClick : function(e)
12482     {
12483         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12484             this.onTickableFooterButtonClick(e, false, false);
12485             return;
12486         }
12487         
12488         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12489             return;
12490         }
12491         
12492         this.page = 0;
12493         this.loadNext = false;
12494         this.hasFocus = true;
12495         
12496         if(this.triggerAction == 'all') {
12497             this.doQuery(this.allQuery, true);
12498         } else {
12499             this.doQuery(this.getRawValue());
12500         }
12501     },
12502     
12503     listKeyPress : function(e)
12504     {
12505         //Roo.log('listkeypress');
12506         // scroll to first matching element based on key pres..
12507         if (e.isSpecialKey()) {
12508             return false;
12509         }
12510         var k = String.fromCharCode(e.getKey()).toUpperCase();
12511         //Roo.log(k);
12512         var match  = false;
12513         var csel = this.view.getSelectedNodes();
12514         var cselitem = false;
12515         if (csel.length) {
12516             var ix = this.view.indexOf(csel[0]);
12517             cselitem  = this.store.getAt(ix);
12518             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12519                 cselitem = false;
12520             }
12521             
12522         }
12523         
12524         this.store.each(function(v) { 
12525             if (cselitem) {
12526                 // start at existing selection.
12527                 if (cselitem.id == v.id) {
12528                     cselitem = false;
12529                 }
12530                 return true;
12531             }
12532                 
12533             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12534                 match = this.store.indexOf(v);
12535                 return false;
12536             }
12537             return true;
12538         }, this);
12539         
12540         if (match === false) {
12541             return true; // no more action?
12542         }
12543         // scroll to?
12544         this.view.select(match);
12545         var sn = Roo.get(this.view.getSelectedNodes()[0])
12546         sn.scrollIntoView(sn.dom.parentNode, false);
12547     },
12548     
12549     onViewScroll : function(e, t){
12550         
12551         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){
12552             return;
12553         }
12554         
12555         this.hasQuery = true;
12556         
12557         this.loading = this.list.select('.loading', true).first();
12558         
12559         if(this.loading === null){
12560             this.list.createChild({
12561                 tag: 'div',
12562                 cls: 'loading select2-more-results select2-active',
12563                 html: 'Loading more results...'
12564             })
12565             
12566             this.loading = this.list.select('.loading', true).first();
12567             
12568             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12569             
12570             this.loading.hide();
12571         }
12572         
12573         this.loading.show();
12574         
12575         var _combo = this;
12576         
12577         this.page++;
12578         this.loadNext = true;
12579         
12580         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12581         
12582         return;
12583     },
12584     
12585     addItem : function(o)
12586     {   
12587         var dv = ''; // display value
12588         
12589         if (this.displayField) {
12590             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12591         } else {
12592             // this is an error condition!!!
12593             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12594         }
12595         
12596         if(!dv.length){
12597             return;
12598         }
12599         
12600         var choice = this.choices.createChild({
12601             tag: 'li',
12602             cls: 'select2-search-choice',
12603             cn: [
12604                 {
12605                     tag: 'div',
12606                     html: dv
12607                 },
12608                 {
12609                     tag: 'a',
12610                     href: '#',
12611                     cls: 'select2-search-choice-close',
12612                     tabindex: '-1'
12613                 }
12614             ]
12615             
12616         }, this.searchField);
12617         
12618         var close = choice.select('a.select2-search-choice-close', true).first()
12619         
12620         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12621         
12622         this.item.push(o);
12623         
12624         this.lastData = o;
12625         
12626         this.syncValue();
12627         
12628         this.inputEl().dom.value = '';
12629         
12630         this.validate();
12631     },
12632     
12633     onRemoveItem : function(e, _self, o)
12634     {
12635         e.preventDefault();
12636         
12637         this.lastItem = Roo.apply([], this.item);
12638         
12639         var index = this.item.indexOf(o.data) * 1;
12640         
12641         if( index < 0){
12642             Roo.log('not this item?!');
12643             return;
12644         }
12645         
12646         this.item.splice(index, 1);
12647         o.item.remove();
12648         
12649         this.syncValue();
12650         
12651         this.fireEvent('remove', this, e);
12652         
12653         this.validate();
12654         
12655     },
12656     
12657     syncValue : function()
12658     {
12659         if(!this.item.length){
12660             this.clearValue();
12661             return;
12662         }
12663             
12664         var value = [];
12665         var _this = this;
12666         Roo.each(this.item, function(i){
12667             if(_this.valueField){
12668                 value.push(i[_this.valueField]);
12669                 return;
12670             }
12671
12672             value.push(i);
12673         });
12674
12675         this.value = value.join(',');
12676
12677         if(this.hiddenField){
12678             this.hiddenField.dom.value = this.value;
12679         }
12680         
12681         this.store.fireEvent("datachanged", this.store);
12682     },
12683     
12684     clearItem : function()
12685     {
12686         if(!this.multiple){
12687             return;
12688         }
12689         
12690         this.item = [];
12691         
12692         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12693            c.remove();
12694         });
12695         
12696         this.syncValue();
12697         
12698         this.validate();
12699     },
12700     
12701     inputEl: function ()
12702     {
12703         if(this.tickable){
12704             return this.searchField;
12705         }
12706         return this.el.select('input.form-control',true).first();
12707     },
12708     
12709     
12710     onTickableFooterButtonClick : function(e, btn, el)
12711     {
12712         e.preventDefault();
12713         
12714         this.lastItem = Roo.apply([], this.item);
12715         
12716         if(btn && btn.name == 'cancel'){
12717             this.tickItems = Roo.apply([], this.item);
12718             this.collapse();
12719             return;
12720         }
12721         
12722         this.clearItem();
12723         
12724         var _this = this;
12725         
12726         Roo.each(this.tickItems, function(o){
12727             _this.addItem(o);
12728         });
12729         
12730         this.collapse();
12731         
12732     },
12733     
12734     validate : function()
12735     {
12736         var v = this.getRawValue();
12737         
12738         if(this.multiple){
12739             v = this.getValue();
12740         }
12741         
12742         if(this.disabled || this.allowBlank || v.length){
12743             this.markValid();
12744             return true;
12745         }
12746         
12747         this.markInvalid();
12748         return false;
12749     },
12750     
12751     tickableInputEl : function()
12752     {
12753         if(!this.tickable || !this.editable){
12754             return this.inputEl();
12755         }
12756         
12757         return this.inputEl().select('.select2-search-field-input', true).first();
12758     }
12759     
12760     
12761
12762     /** 
12763     * @cfg {Boolean} grow 
12764     * @hide 
12765     */
12766     /** 
12767     * @cfg {Number} growMin 
12768     * @hide 
12769     */
12770     /** 
12771     * @cfg {Number} growMax 
12772     * @hide 
12773     */
12774     /**
12775      * @hide
12776      * @method autoSize
12777      */
12778 });
12779 /*
12780  * Based on:
12781  * Ext JS Library 1.1.1
12782  * Copyright(c) 2006-2007, Ext JS, LLC.
12783  *
12784  * Originally Released Under LGPL - original licence link has changed is not relivant.
12785  *
12786  * Fork - LGPL
12787  * <script type="text/javascript">
12788  */
12789
12790 /**
12791  * @class Roo.View
12792  * @extends Roo.util.Observable
12793  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12794  * This class also supports single and multi selection modes. <br>
12795  * Create a data model bound view:
12796  <pre><code>
12797  var store = new Roo.data.Store(...);
12798
12799  var view = new Roo.View({
12800     el : "my-element",
12801     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12802  
12803     singleSelect: true,
12804     selectedClass: "ydataview-selected",
12805     store: store
12806  });
12807
12808  // listen for node click?
12809  view.on("click", function(vw, index, node, e){
12810  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12811  });
12812
12813  // load XML data
12814  dataModel.load("foobar.xml");
12815  </code></pre>
12816  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12817  * <br><br>
12818  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12819  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12820  * 
12821  * Note: old style constructor is still suported (container, template, config)
12822  * 
12823  * @constructor
12824  * Create a new View
12825  * @param {Object} config The config object
12826  * 
12827  */
12828 Roo.View = function(config, depreciated_tpl, depreciated_config){
12829     
12830     this.parent = false;
12831     
12832     if (typeof(depreciated_tpl) == 'undefined') {
12833         // new way.. - universal constructor.
12834         Roo.apply(this, config);
12835         this.el  = Roo.get(this.el);
12836     } else {
12837         // old format..
12838         this.el  = Roo.get(config);
12839         this.tpl = depreciated_tpl;
12840         Roo.apply(this, depreciated_config);
12841     }
12842     this.wrapEl  = this.el.wrap().wrap();
12843     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12844     
12845     
12846     if(typeof(this.tpl) == "string"){
12847         this.tpl = new Roo.Template(this.tpl);
12848     } else {
12849         // support xtype ctors..
12850         this.tpl = new Roo.factory(this.tpl, Roo);
12851     }
12852     
12853     
12854     this.tpl.compile();
12855     
12856     /** @private */
12857     this.addEvents({
12858         /**
12859          * @event beforeclick
12860          * Fires before a click is processed. Returns false to cancel the default action.
12861          * @param {Roo.View} this
12862          * @param {Number} index The index of the target node
12863          * @param {HTMLElement} node The target node
12864          * @param {Roo.EventObject} e The raw event object
12865          */
12866             "beforeclick" : true,
12867         /**
12868          * @event click
12869          * Fires when a template node is clicked.
12870          * @param {Roo.View} this
12871          * @param {Number} index The index of the target node
12872          * @param {HTMLElement} node The target node
12873          * @param {Roo.EventObject} e The raw event object
12874          */
12875             "click" : true,
12876         /**
12877          * @event dblclick
12878          * Fires when a template node is double clicked.
12879          * @param {Roo.View} this
12880          * @param {Number} index The index of the target node
12881          * @param {HTMLElement} node The target node
12882          * @param {Roo.EventObject} e The raw event object
12883          */
12884             "dblclick" : true,
12885         /**
12886          * @event contextmenu
12887          * Fires when a template node is right clicked.
12888          * @param {Roo.View} this
12889          * @param {Number} index The index of the target node
12890          * @param {HTMLElement} node The target node
12891          * @param {Roo.EventObject} e The raw event object
12892          */
12893             "contextmenu" : true,
12894         /**
12895          * @event selectionchange
12896          * Fires when the selected nodes change.
12897          * @param {Roo.View} this
12898          * @param {Array} selections Array of the selected nodes
12899          */
12900             "selectionchange" : true,
12901     
12902         /**
12903          * @event beforeselect
12904          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12905          * @param {Roo.View} this
12906          * @param {HTMLElement} node The node to be selected
12907          * @param {Array} selections Array of currently selected nodes
12908          */
12909             "beforeselect" : true,
12910         /**
12911          * @event preparedata
12912          * Fires on every row to render, to allow you to change the data.
12913          * @param {Roo.View} this
12914          * @param {Object} data to be rendered (change this)
12915          */
12916           "preparedata" : true
12917           
12918           
12919         });
12920
12921
12922
12923     this.el.on({
12924         "click": this.onClick,
12925         "dblclick": this.onDblClick,
12926         "contextmenu": this.onContextMenu,
12927         scope:this
12928     });
12929
12930     this.selections = [];
12931     this.nodes = [];
12932     this.cmp = new Roo.CompositeElementLite([]);
12933     if(this.store){
12934         this.store = Roo.factory(this.store, Roo.data);
12935         this.setStore(this.store, true);
12936     }
12937     
12938     if ( this.footer && this.footer.xtype) {
12939            
12940          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12941         
12942         this.footer.dataSource = this.store
12943         this.footer.container = fctr;
12944         this.footer = Roo.factory(this.footer, Roo);
12945         fctr.insertFirst(this.el);
12946         
12947         // this is a bit insane - as the paging toolbar seems to detach the el..
12948 //        dom.parentNode.parentNode.parentNode
12949          // they get detached?
12950     }
12951     
12952     
12953     Roo.View.superclass.constructor.call(this);
12954     
12955     
12956 };
12957
12958 Roo.extend(Roo.View, Roo.util.Observable, {
12959     
12960      /**
12961      * @cfg {Roo.data.Store} store Data store to load data from.
12962      */
12963     store : false,
12964     
12965     /**
12966      * @cfg {String|Roo.Element} el The container element.
12967      */
12968     el : '',
12969     
12970     /**
12971      * @cfg {String|Roo.Template} tpl The template used by this View 
12972      */
12973     tpl : false,
12974     /**
12975      * @cfg {String} dataName the named area of the template to use as the data area
12976      *                          Works with domtemplates roo-name="name"
12977      */
12978     dataName: false,
12979     /**
12980      * @cfg {String} selectedClass The css class to add to selected nodes
12981      */
12982     selectedClass : "x-view-selected",
12983      /**
12984      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12985      */
12986     emptyText : "",
12987     
12988     /**
12989      * @cfg {String} text to display on mask (default Loading)
12990      */
12991     mask : false,
12992     /**
12993      * @cfg {Boolean} multiSelect Allow multiple selection
12994      */
12995     multiSelect : false,
12996     /**
12997      * @cfg {Boolean} singleSelect Allow single selection
12998      */
12999     singleSelect:  false,
13000     
13001     /**
13002      * @cfg {Boolean} toggleSelect - selecting 
13003      */
13004     toggleSelect : false,
13005     
13006     /**
13007      * @cfg {Boolean} tickable - selecting 
13008      */
13009     tickable : false,
13010     
13011     /**
13012      * Returns the element this view is bound to.
13013      * @return {Roo.Element}
13014      */
13015     getEl : function(){
13016         return this.wrapEl;
13017     },
13018     
13019     
13020
13021     /**
13022      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13023      */
13024     refresh : function(){
13025         //Roo.log('refresh');
13026         var t = this.tpl;
13027         
13028         // if we are using something like 'domtemplate', then
13029         // the what gets used is:
13030         // t.applySubtemplate(NAME, data, wrapping data..)
13031         // the outer template then get' applied with
13032         //     the store 'extra data'
13033         // and the body get's added to the
13034         //      roo-name="data" node?
13035         //      <span class='roo-tpl-{name}'></span> ?????
13036         
13037         
13038         
13039         this.clearSelections();
13040         this.el.update("");
13041         var html = [];
13042         var records = this.store.getRange();
13043         if(records.length < 1) {
13044             
13045             // is this valid??  = should it render a template??
13046             
13047             this.el.update(this.emptyText);
13048             return;
13049         }
13050         var el = this.el;
13051         if (this.dataName) {
13052             this.el.update(t.apply(this.store.meta)); //????
13053             el = this.el.child('.roo-tpl-' + this.dataName);
13054         }
13055         
13056         for(var i = 0, len = records.length; i < len; i++){
13057             var data = this.prepareData(records[i].data, i, records[i]);
13058             this.fireEvent("preparedata", this, data, i, records[i]);
13059             
13060             var d = Roo.apply({}, data);
13061             
13062             if(this.tickable){
13063                 Roo.apply(d, {'roo-id' : Roo.id()});
13064                 
13065                 var _this = this;
13066             
13067                 Roo.each(this.parent.item, function(item){
13068                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13069                         return;
13070                     }
13071                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13072                 });
13073             }
13074             
13075             html[html.length] = Roo.util.Format.trim(
13076                 this.dataName ?
13077                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13078                     t.apply(d)
13079             );
13080         }
13081         
13082         
13083         
13084         el.update(html.join(""));
13085         this.nodes = el.dom.childNodes;
13086         this.updateIndexes(0);
13087     },
13088     
13089
13090     /**
13091      * Function to override to reformat the data that is sent to
13092      * the template for each node.
13093      * DEPRICATED - use the preparedata event handler.
13094      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13095      * a JSON object for an UpdateManager bound view).
13096      */
13097     prepareData : function(data, index, record)
13098     {
13099         this.fireEvent("preparedata", this, data, index, record);
13100         return data;
13101     },
13102
13103     onUpdate : function(ds, record){
13104         // Roo.log('on update');   
13105         this.clearSelections();
13106         var index = this.store.indexOf(record);
13107         var n = this.nodes[index];
13108         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13109         n.parentNode.removeChild(n);
13110         this.updateIndexes(index, index);
13111     },
13112
13113     
13114     
13115 // --------- FIXME     
13116     onAdd : function(ds, records, index)
13117     {
13118         //Roo.log(['on Add', ds, records, index] );        
13119         this.clearSelections();
13120         if(this.nodes.length == 0){
13121             this.refresh();
13122             return;
13123         }
13124         var n = this.nodes[index];
13125         for(var i = 0, len = records.length; i < len; i++){
13126             var d = this.prepareData(records[i].data, i, records[i]);
13127             if(n){
13128                 this.tpl.insertBefore(n, d);
13129             }else{
13130                 
13131                 this.tpl.append(this.el, d);
13132             }
13133         }
13134         this.updateIndexes(index);
13135     },
13136
13137     onRemove : function(ds, record, index){
13138        // Roo.log('onRemove');
13139         this.clearSelections();
13140         var el = this.dataName  ?
13141             this.el.child('.roo-tpl-' + this.dataName) :
13142             this.el; 
13143         
13144         el.dom.removeChild(this.nodes[index]);
13145         this.updateIndexes(index);
13146     },
13147
13148     /**
13149      * Refresh an individual node.
13150      * @param {Number} index
13151      */
13152     refreshNode : function(index){
13153         this.onUpdate(this.store, this.store.getAt(index));
13154     },
13155
13156     updateIndexes : function(startIndex, endIndex){
13157         var ns = this.nodes;
13158         startIndex = startIndex || 0;
13159         endIndex = endIndex || ns.length - 1;
13160         for(var i = startIndex; i <= endIndex; i++){
13161             ns[i].nodeIndex = i;
13162         }
13163     },
13164
13165     /**
13166      * Changes the data store this view uses and refresh the view.
13167      * @param {Store} store
13168      */
13169     setStore : function(store, initial){
13170         if(!initial && this.store){
13171             this.store.un("datachanged", this.refresh);
13172             this.store.un("add", this.onAdd);
13173             this.store.un("remove", this.onRemove);
13174             this.store.un("update", this.onUpdate);
13175             this.store.un("clear", this.refresh);
13176             this.store.un("beforeload", this.onBeforeLoad);
13177             this.store.un("load", this.onLoad);
13178             this.store.un("loadexception", this.onLoad);
13179         }
13180         if(store){
13181           
13182             store.on("datachanged", this.refresh, this);
13183             store.on("add", this.onAdd, this);
13184             store.on("remove", this.onRemove, this);
13185             store.on("update", this.onUpdate, this);
13186             store.on("clear", this.refresh, this);
13187             store.on("beforeload", this.onBeforeLoad, this);
13188             store.on("load", this.onLoad, this);
13189             store.on("loadexception", this.onLoad, this);
13190         }
13191         
13192         if(store){
13193             this.refresh();
13194         }
13195     },
13196     /**
13197      * onbeforeLoad - masks the loading area.
13198      *
13199      */
13200     onBeforeLoad : function(store,opts)
13201     {
13202          //Roo.log('onBeforeLoad');   
13203         if (!opts.add) {
13204             this.el.update("");
13205         }
13206         this.el.mask(this.mask ? this.mask : "Loading" ); 
13207     },
13208     onLoad : function ()
13209     {
13210         this.el.unmask();
13211     },
13212     
13213
13214     /**
13215      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13216      * @param {HTMLElement} node
13217      * @return {HTMLElement} The template node
13218      */
13219     findItemFromChild : function(node){
13220         var el = this.dataName  ?
13221             this.el.child('.roo-tpl-' + this.dataName,true) :
13222             this.el.dom; 
13223         
13224         if(!node || node.parentNode == el){
13225                     return node;
13226             }
13227             var p = node.parentNode;
13228             while(p && p != el){
13229             if(p.parentNode == el){
13230                 return p;
13231             }
13232             p = p.parentNode;
13233         }
13234             return null;
13235     },
13236
13237     /** @ignore */
13238     onClick : function(e){
13239         var item = this.findItemFromChild(e.getTarget());
13240         if(item){
13241             var index = this.indexOf(item);
13242             if(this.onItemClick(item, index, e) !== false){
13243                 this.fireEvent("click", this, index, item, e);
13244             }
13245         }else{
13246             this.clearSelections();
13247         }
13248     },
13249
13250     /** @ignore */
13251     onContextMenu : function(e){
13252         var item = this.findItemFromChild(e.getTarget());
13253         if(item){
13254             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13255         }
13256     },
13257
13258     /** @ignore */
13259     onDblClick : function(e){
13260         var item = this.findItemFromChild(e.getTarget());
13261         if(item){
13262             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13263         }
13264     },
13265
13266     onItemClick : function(item, index, e)
13267     {
13268         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13269             return false;
13270         }
13271         if (this.toggleSelect) {
13272             var m = this.isSelected(item) ? 'unselect' : 'select';
13273             //Roo.log(m);
13274             var _t = this;
13275             _t[m](item, true, false);
13276             return true;
13277         }
13278         if(this.multiSelect || this.singleSelect){
13279             if(this.multiSelect && e.shiftKey && this.lastSelection){
13280                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13281             }else{
13282                 this.select(item, this.multiSelect && e.ctrlKey);
13283                 this.lastSelection = item;
13284             }
13285             
13286             if(!this.tickable){
13287                 e.preventDefault();
13288             }
13289             
13290         }
13291         return true;
13292     },
13293
13294     /**
13295      * Get the number of selected nodes.
13296      * @return {Number}
13297      */
13298     getSelectionCount : function(){
13299         return this.selections.length;
13300     },
13301
13302     /**
13303      * Get the currently selected nodes.
13304      * @return {Array} An array of HTMLElements
13305      */
13306     getSelectedNodes : function(){
13307         return this.selections;
13308     },
13309
13310     /**
13311      * Get the indexes of the selected nodes.
13312      * @return {Array}
13313      */
13314     getSelectedIndexes : function(){
13315         var indexes = [], s = this.selections;
13316         for(var i = 0, len = s.length; i < len; i++){
13317             indexes.push(s[i].nodeIndex);
13318         }
13319         return indexes;
13320     },
13321
13322     /**
13323      * Clear all selections
13324      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13325      */
13326     clearSelections : function(suppressEvent){
13327         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13328             this.cmp.elements = this.selections;
13329             this.cmp.removeClass(this.selectedClass);
13330             this.selections = [];
13331             if(!suppressEvent){
13332                 this.fireEvent("selectionchange", this, this.selections);
13333             }
13334         }
13335     },
13336
13337     /**
13338      * Returns true if the passed node is selected
13339      * @param {HTMLElement/Number} node The node or node index
13340      * @return {Boolean}
13341      */
13342     isSelected : function(node){
13343         var s = this.selections;
13344         if(s.length < 1){
13345             return false;
13346         }
13347         node = this.getNode(node);
13348         return s.indexOf(node) !== -1;
13349     },
13350
13351     /**
13352      * Selects nodes.
13353      * @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
13354      * @param {Boolean} keepExisting (optional) true to keep existing selections
13355      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13356      */
13357     select : function(nodeInfo, keepExisting, suppressEvent){
13358         if(nodeInfo instanceof Array){
13359             if(!keepExisting){
13360                 this.clearSelections(true);
13361             }
13362             for(var i = 0, len = nodeInfo.length; i < len; i++){
13363                 this.select(nodeInfo[i], true, true);
13364             }
13365             return;
13366         } 
13367         var node = this.getNode(nodeInfo);
13368         if(!node || this.isSelected(node)){
13369             return; // already selected.
13370         }
13371         if(!keepExisting){
13372             this.clearSelections(true);
13373         }
13374         
13375         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13376             Roo.fly(node).addClass(this.selectedClass);
13377             this.selections.push(node);
13378             if(!suppressEvent){
13379                 this.fireEvent("selectionchange", this, this.selections);
13380             }
13381         }
13382         
13383         
13384     },
13385       /**
13386      * Unselects nodes.
13387      * @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
13388      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13389      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13390      */
13391     unselect : function(nodeInfo, keepExisting, suppressEvent)
13392     {
13393         if(nodeInfo instanceof Array){
13394             Roo.each(this.selections, function(s) {
13395                 this.unselect(s, nodeInfo);
13396             }, this);
13397             return;
13398         }
13399         var node = this.getNode(nodeInfo);
13400         if(!node || !this.isSelected(node)){
13401             //Roo.log("not selected");
13402             return; // not selected.
13403         }
13404         // fireevent???
13405         var ns = [];
13406         Roo.each(this.selections, function(s) {
13407             if (s == node ) {
13408                 Roo.fly(node).removeClass(this.selectedClass);
13409
13410                 return;
13411             }
13412             ns.push(s);
13413         },this);
13414         
13415         this.selections= ns;
13416         this.fireEvent("selectionchange", this, this.selections);
13417     },
13418
13419     /**
13420      * Gets a template node.
13421      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13422      * @return {HTMLElement} The node or null if it wasn't found
13423      */
13424     getNode : function(nodeInfo){
13425         if(typeof nodeInfo == "string"){
13426             return document.getElementById(nodeInfo);
13427         }else if(typeof nodeInfo == "number"){
13428             return this.nodes[nodeInfo];
13429         }
13430         return nodeInfo;
13431     },
13432
13433     /**
13434      * Gets a range template nodes.
13435      * @param {Number} startIndex
13436      * @param {Number} endIndex
13437      * @return {Array} An array of nodes
13438      */
13439     getNodes : function(start, end){
13440         var ns = this.nodes;
13441         start = start || 0;
13442         end = typeof end == "undefined" ? ns.length - 1 : end;
13443         var nodes = [];
13444         if(start <= end){
13445             for(var i = start; i <= end; i++){
13446                 nodes.push(ns[i]);
13447             }
13448         } else{
13449             for(var i = start; i >= end; i--){
13450                 nodes.push(ns[i]);
13451             }
13452         }
13453         return nodes;
13454     },
13455
13456     /**
13457      * Finds the index of the passed node
13458      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13459      * @return {Number} The index of the node or -1
13460      */
13461     indexOf : function(node){
13462         node = this.getNode(node);
13463         if(typeof node.nodeIndex == "number"){
13464             return node.nodeIndex;
13465         }
13466         var ns = this.nodes;
13467         for(var i = 0, len = ns.length; i < len; i++){
13468             if(ns[i] == node){
13469                 return i;
13470             }
13471         }
13472         return -1;
13473     }
13474 });
13475 /*
13476  * - LGPL
13477  *
13478  * based on jquery fullcalendar
13479  * 
13480  */
13481
13482 Roo.bootstrap = Roo.bootstrap || {};
13483 /**
13484  * @class Roo.bootstrap.Calendar
13485  * @extends Roo.bootstrap.Component
13486  * Bootstrap Calendar class
13487  * @cfg {Boolean} loadMask (true|false) default false
13488  * @cfg {Object} header generate the user specific header of the calendar, default false
13489
13490  * @constructor
13491  * Create a new Container
13492  * @param {Object} config The config object
13493  */
13494
13495
13496
13497 Roo.bootstrap.Calendar = function(config){
13498     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13499      this.addEvents({
13500         /**
13501              * @event select
13502              * Fires when a date is selected
13503              * @param {DatePicker} this
13504              * @param {Date} date The selected date
13505              */
13506         'select': true,
13507         /**
13508              * @event monthchange
13509              * Fires when the displayed month changes 
13510              * @param {DatePicker} this
13511              * @param {Date} date The selected month
13512              */
13513         'monthchange': true,
13514         /**
13515              * @event evententer
13516              * Fires when mouse over an event
13517              * @param {Calendar} this
13518              * @param {event} Event
13519              */
13520         'evententer': true,
13521         /**
13522              * @event eventleave
13523              * Fires when the mouse leaves an
13524              * @param {Calendar} this
13525              * @param {event}
13526              */
13527         'eventleave': true,
13528         /**
13529              * @event eventclick
13530              * Fires when the mouse click an
13531              * @param {Calendar} this
13532              * @param {event}
13533              */
13534         'eventclick': true
13535         
13536     });
13537
13538 };
13539
13540 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13541     
13542      /**
13543      * @cfg {Number} startDay
13544      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13545      */
13546     startDay : 0,
13547     
13548     loadMask : false,
13549     
13550     header : false,
13551       
13552     getAutoCreate : function(){
13553         
13554         
13555         var fc_button = function(name, corner, style, content ) {
13556             return Roo.apply({},{
13557                 tag : 'span',
13558                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13559                          (corner.length ?
13560                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13561                             ''
13562                         ),
13563                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13564                 unselectable: 'on'
13565             });
13566         };
13567         
13568         var header = {};
13569         
13570         if(!this.header){
13571             header = {
13572                 tag : 'table',
13573                 cls : 'fc-header',
13574                 style : 'width:100%',
13575                 cn : [
13576                     {
13577                         tag: 'tr',
13578                         cn : [
13579                             {
13580                                 tag : 'td',
13581                                 cls : 'fc-header-left',
13582                                 cn : [
13583                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13584                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13585                                     { tag: 'span', cls: 'fc-header-space' },
13586                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13587
13588
13589                                 ]
13590                             },
13591
13592                             {
13593                                 tag : 'td',
13594                                 cls : 'fc-header-center',
13595                                 cn : [
13596                                     {
13597                                         tag: 'span',
13598                                         cls: 'fc-header-title',
13599                                         cn : {
13600                                             tag: 'H2',
13601                                             html : 'month / year'
13602                                         }
13603                                     }
13604
13605                                 ]
13606                             },
13607                             {
13608                                 tag : 'td',
13609                                 cls : 'fc-header-right',
13610                                 cn : [
13611                               /*      fc_button('month', 'left', '', 'month' ),
13612                                     fc_button('week', '', '', 'week' ),
13613                                     fc_button('day', 'right', '', 'day' )
13614                                 */    
13615
13616                                 ]
13617                             }
13618
13619                         ]
13620                     }
13621                 ]
13622             };
13623         }
13624         
13625         header = this.header;
13626         
13627        
13628         var cal_heads = function() {
13629             var ret = [];
13630             // fixme - handle this.
13631             
13632             for (var i =0; i < Date.dayNames.length; i++) {
13633                 var d = Date.dayNames[i];
13634                 ret.push({
13635                     tag: 'th',
13636                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13637                     html : d.substring(0,3)
13638                 });
13639                 
13640             }
13641             ret[0].cls += ' fc-first';
13642             ret[6].cls += ' fc-last';
13643             return ret;
13644         };
13645         var cal_cell = function(n) {
13646             return  {
13647                 tag: 'td',
13648                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13649                 cn : [
13650                     {
13651                         cn : [
13652                             {
13653                                 cls: 'fc-day-number',
13654                                 html: 'D'
13655                             },
13656                             {
13657                                 cls: 'fc-day-content',
13658                              
13659                                 cn : [
13660                                      {
13661                                         style: 'position: relative;' // height: 17px;
13662                                     }
13663                                 ]
13664                             }
13665                             
13666                             
13667                         ]
13668                     }
13669                 ]
13670                 
13671             }
13672         };
13673         var cal_rows = function() {
13674             
13675             var ret = [];
13676             for (var r = 0; r < 6; r++) {
13677                 var row= {
13678                     tag : 'tr',
13679                     cls : 'fc-week',
13680                     cn : []
13681                 };
13682                 
13683                 for (var i =0; i < Date.dayNames.length; i++) {
13684                     var d = Date.dayNames[i];
13685                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13686
13687                 }
13688                 row.cn[0].cls+=' fc-first';
13689                 row.cn[0].cn[0].style = 'min-height:90px';
13690                 row.cn[6].cls+=' fc-last';
13691                 ret.push(row);
13692                 
13693             }
13694             ret[0].cls += ' fc-first';
13695             ret[4].cls += ' fc-prev-last';
13696             ret[5].cls += ' fc-last';
13697             return ret;
13698             
13699         };
13700         
13701         var cal_table = {
13702             tag: 'table',
13703             cls: 'fc-border-separate',
13704             style : 'width:100%',
13705             cellspacing  : 0,
13706             cn : [
13707                 { 
13708                     tag: 'thead',
13709                     cn : [
13710                         { 
13711                             tag: 'tr',
13712                             cls : 'fc-first fc-last',
13713                             cn : cal_heads()
13714                         }
13715                     ]
13716                 },
13717                 { 
13718                     tag: 'tbody',
13719                     cn : cal_rows()
13720                 }
13721                   
13722             ]
13723         };
13724          
13725          var cfg = {
13726             cls : 'fc fc-ltr',
13727             cn : [
13728                 header,
13729                 {
13730                     cls : 'fc-content',
13731                     style : "position: relative;",
13732                     cn : [
13733                         {
13734                             cls : 'fc-view fc-view-month fc-grid',
13735                             style : 'position: relative',
13736                             unselectable : 'on',
13737                             cn : [
13738                                 {
13739                                     cls : 'fc-event-container',
13740                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13741                                 },
13742                                 cal_table
13743                             ]
13744                         }
13745                     ]
13746     
13747                 }
13748            ] 
13749             
13750         };
13751         
13752          
13753         
13754         return cfg;
13755     },
13756     
13757     
13758     initEvents : function()
13759     {
13760         if(!this.store){
13761             throw "can not find store for calendar";
13762         }
13763         
13764         var mark = {
13765             tag: "div",
13766             cls:"x-dlg-mask",
13767             style: "text-align:center",
13768             cn: [
13769                 {
13770                     tag: "div",
13771                     style: "background-color:white;width:50%;margin:250 auto",
13772                     cn: [
13773                         {
13774                             tag: "img",
13775                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13776                         },
13777                         {
13778                             tag: "span",
13779                             html: "Loading"
13780                         }
13781                         
13782                     ]
13783                 }
13784             ]
13785         }
13786         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13787         
13788         var size = this.el.select('.fc-content', true).first().getSize();
13789         this.maskEl.setSize(size.width, size.height);
13790         this.maskEl.enableDisplayMode("block");
13791         if(!this.loadMask){
13792             this.maskEl.hide();
13793         }
13794         
13795         this.store = Roo.factory(this.store, Roo.data);
13796         this.store.on('load', this.onLoad, this);
13797         this.store.on('beforeload', this.onBeforeLoad, this);
13798         
13799         this.resize();
13800         
13801         this.cells = this.el.select('.fc-day',true);
13802         //Roo.log(this.cells);
13803         this.textNodes = this.el.query('.fc-day-number');
13804         this.cells.addClassOnOver('fc-state-hover');
13805         
13806         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13807         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13808         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13809         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13810         
13811         this.on('monthchange', this.onMonthChange, this);
13812         
13813         this.update(new Date().clearTime());
13814     },
13815     
13816     resize : function() {
13817         var sz  = this.el.getSize();
13818         
13819         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13820         this.el.select('.fc-day-content div',true).setHeight(34);
13821     },
13822     
13823     
13824     // private
13825     showPrevMonth : function(e){
13826         this.update(this.activeDate.add("mo", -1));
13827     },
13828     showToday : function(e){
13829         this.update(new Date().clearTime());
13830     },
13831     // private
13832     showNextMonth : function(e){
13833         this.update(this.activeDate.add("mo", 1));
13834     },
13835
13836     // private
13837     showPrevYear : function(){
13838         this.update(this.activeDate.add("y", -1));
13839     },
13840
13841     // private
13842     showNextYear : function(){
13843         this.update(this.activeDate.add("y", 1));
13844     },
13845
13846     
13847    // private
13848     update : function(date)
13849     {
13850         var vd = this.activeDate;
13851         this.activeDate = date;
13852 //        if(vd && this.el){
13853 //            var t = date.getTime();
13854 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13855 //                Roo.log('using add remove');
13856 //                
13857 //                this.fireEvent('monthchange', this, date);
13858 //                
13859 //                this.cells.removeClass("fc-state-highlight");
13860 //                this.cells.each(function(c){
13861 //                   if(c.dateValue == t){
13862 //                       c.addClass("fc-state-highlight");
13863 //                       setTimeout(function(){
13864 //                            try{c.dom.firstChild.focus();}catch(e){}
13865 //                       }, 50);
13866 //                       return false;
13867 //                   }
13868 //                   return true;
13869 //                });
13870 //                return;
13871 //            }
13872 //        }
13873         
13874         var days = date.getDaysInMonth();
13875         
13876         var firstOfMonth = date.getFirstDateOfMonth();
13877         var startingPos = firstOfMonth.getDay()-this.startDay;
13878         
13879         if(startingPos < this.startDay){
13880             startingPos += 7;
13881         }
13882         
13883         var pm = date.add(Date.MONTH, -1);
13884         var prevStart = pm.getDaysInMonth()-startingPos;
13885 //        
13886         this.cells = this.el.select('.fc-day',true);
13887         this.textNodes = this.el.query('.fc-day-number');
13888         this.cells.addClassOnOver('fc-state-hover');
13889         
13890         var cells = this.cells.elements;
13891         var textEls = this.textNodes;
13892         
13893         Roo.each(cells, function(cell){
13894             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13895         });
13896         
13897         days += startingPos;
13898
13899         // convert everything to numbers so it's fast
13900         var day = 86400000;
13901         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13902         //Roo.log(d);
13903         //Roo.log(pm);
13904         //Roo.log(prevStart);
13905         
13906         var today = new Date().clearTime().getTime();
13907         var sel = date.clearTime().getTime();
13908         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13909         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13910         var ddMatch = this.disabledDatesRE;
13911         var ddText = this.disabledDatesText;
13912         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13913         var ddaysText = this.disabledDaysText;
13914         var format = this.format;
13915         
13916         var setCellClass = function(cal, cell){
13917             cell.row = 0;
13918             cell.events = [];
13919             cell.more = [];
13920             //Roo.log('set Cell Class');
13921             cell.title = "";
13922             var t = d.getTime();
13923             
13924             //Roo.log(d);
13925             
13926             cell.dateValue = t;
13927             if(t == today){
13928                 cell.className += " fc-today";
13929                 cell.className += " fc-state-highlight";
13930                 cell.title = cal.todayText;
13931             }
13932             if(t == sel){
13933                 // disable highlight in other month..
13934                 //cell.className += " fc-state-highlight";
13935                 
13936             }
13937             // disabling
13938             if(t < min) {
13939                 cell.className = " fc-state-disabled";
13940                 cell.title = cal.minText;
13941                 return;
13942             }
13943             if(t > max) {
13944                 cell.className = " fc-state-disabled";
13945                 cell.title = cal.maxText;
13946                 return;
13947             }
13948             if(ddays){
13949                 if(ddays.indexOf(d.getDay()) != -1){
13950                     cell.title = ddaysText;
13951                     cell.className = " fc-state-disabled";
13952                 }
13953             }
13954             if(ddMatch && format){
13955                 var fvalue = d.dateFormat(format);
13956                 if(ddMatch.test(fvalue)){
13957                     cell.title = ddText.replace("%0", fvalue);
13958                     cell.className = " fc-state-disabled";
13959                 }
13960             }
13961             
13962             if (!cell.initialClassName) {
13963                 cell.initialClassName = cell.dom.className;
13964             }
13965             
13966             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13967         };
13968
13969         var i = 0;
13970         
13971         for(; i < startingPos; i++) {
13972             textEls[i].innerHTML = (++prevStart);
13973             d.setDate(d.getDate()+1);
13974             
13975             cells[i].className = "fc-past fc-other-month";
13976             setCellClass(this, cells[i]);
13977         }
13978         
13979         var intDay = 0;
13980         
13981         for(; i < days; i++){
13982             intDay = i - startingPos + 1;
13983             textEls[i].innerHTML = (intDay);
13984             d.setDate(d.getDate()+1);
13985             
13986             cells[i].className = ''; // "x-date-active";
13987             setCellClass(this, cells[i]);
13988         }
13989         var extraDays = 0;
13990         
13991         for(; i < 42; i++) {
13992             textEls[i].innerHTML = (++extraDays);
13993             d.setDate(d.getDate()+1);
13994             
13995             cells[i].className = "fc-future fc-other-month";
13996             setCellClass(this, cells[i]);
13997         }
13998         
13999         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14000         
14001         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14002         
14003         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14004         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14005         
14006         if(totalRows != 6){
14007             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14008             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14009         }
14010         
14011         this.fireEvent('monthchange', this, date);
14012         
14013         
14014         /*
14015         if(!this.internalRender){
14016             var main = this.el.dom.firstChild;
14017             var w = main.offsetWidth;
14018             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14019             Roo.fly(main).setWidth(w);
14020             this.internalRender = true;
14021             // opera does not respect the auto grow header center column
14022             // then, after it gets a width opera refuses to recalculate
14023             // without a second pass
14024             if(Roo.isOpera && !this.secondPass){
14025                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14026                 this.secondPass = true;
14027                 this.update.defer(10, this, [date]);
14028             }
14029         }
14030         */
14031         
14032     },
14033     
14034     findCell : function(dt) {
14035         dt = dt.clearTime().getTime();
14036         var ret = false;
14037         this.cells.each(function(c){
14038             //Roo.log("check " +c.dateValue + '?=' + dt);
14039             if(c.dateValue == dt){
14040                 ret = c;
14041                 return false;
14042             }
14043             return true;
14044         });
14045         
14046         return ret;
14047     },
14048     
14049     findCells : function(ev) {
14050         var s = ev.start.clone().clearTime().getTime();
14051        // Roo.log(s);
14052         var e= ev.end.clone().clearTime().getTime();
14053        // Roo.log(e);
14054         var ret = [];
14055         this.cells.each(function(c){
14056              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14057             
14058             if(c.dateValue > e){
14059                 return ;
14060             }
14061             if(c.dateValue < s){
14062                 return ;
14063             }
14064             ret.push(c);
14065         });
14066         
14067         return ret;    
14068     },
14069     
14070 //    findBestRow: function(cells)
14071 //    {
14072 //        var ret = 0;
14073 //        
14074 //        for (var i =0 ; i < cells.length;i++) {
14075 //            ret  = Math.max(cells[i].rows || 0,ret);
14076 //        }
14077 //        return ret;
14078 //        
14079 //    },
14080     
14081     
14082     addItem : function(ev)
14083     {
14084         // look for vertical location slot in
14085         var cells = this.findCells(ev);
14086         
14087 //        ev.row = this.findBestRow(cells);
14088         
14089         // work out the location.
14090         
14091         var crow = false;
14092         var rows = [];
14093         for(var i =0; i < cells.length; i++) {
14094             
14095             cells[i].row = cells[0].row;
14096             
14097             if(i == 0){
14098                 cells[i].row = cells[i].row + 1;
14099             }
14100             
14101             if (!crow) {
14102                 crow = {
14103                     start : cells[i],
14104                     end :  cells[i]
14105                 };
14106                 continue;
14107             }
14108             if (crow.start.getY() == cells[i].getY()) {
14109                 // on same row.
14110                 crow.end = cells[i];
14111                 continue;
14112             }
14113             // different row.
14114             rows.push(crow);
14115             crow = {
14116                 start: cells[i],
14117                 end : cells[i]
14118             };
14119             
14120         }
14121         
14122         rows.push(crow);
14123         ev.els = [];
14124         ev.rows = rows;
14125         ev.cells = cells;
14126         
14127         cells[0].events.push(ev);
14128         
14129         this.calevents.push(ev);
14130     },
14131     
14132     clearEvents: function() {
14133         
14134         if(!this.calevents){
14135             return;
14136         }
14137         
14138         Roo.each(this.cells.elements, function(c){
14139             c.row = 0;
14140             c.events = [];
14141             c.more = [];
14142         });
14143         
14144         Roo.each(this.calevents, function(e) {
14145             Roo.each(e.els, function(el) {
14146                 el.un('mouseenter' ,this.onEventEnter, this);
14147                 el.un('mouseleave' ,this.onEventLeave, this);
14148                 el.remove();
14149             },this);
14150         },this);
14151         
14152         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14153             e.remove();
14154         });
14155         
14156     },
14157     
14158     renderEvents: function()
14159     {   
14160         var _this = this;
14161         
14162         this.cells.each(function(c) {
14163             
14164             if(c.row < 5){
14165                 return;
14166             }
14167             
14168             var ev = c.events;
14169             
14170             var r = 4;
14171             if(c.row != c.events.length){
14172                 r = 4 - (4 - (c.row - c.events.length));
14173             }
14174             
14175             c.events = ev.slice(0, r);
14176             c.more = ev.slice(r);
14177             
14178             if(c.more.length && c.more.length == 1){
14179                 c.events.push(c.more.pop());
14180             }
14181             
14182             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14183             
14184         });
14185             
14186         this.cells.each(function(c) {
14187             
14188             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14189             
14190             
14191             for (var e = 0; e < c.events.length; e++){
14192                 var ev = c.events[e];
14193                 var rows = ev.rows;
14194                 
14195                 for(var i = 0; i < rows.length; i++) {
14196                 
14197                     // how many rows should it span..
14198
14199                     var  cfg = {
14200                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14201                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14202
14203                         unselectable : "on",
14204                         cn : [
14205                             {
14206                                 cls: 'fc-event-inner',
14207                                 cn : [
14208     //                                {
14209     //                                  tag:'span',
14210     //                                  cls: 'fc-event-time',
14211     //                                  html : cells.length > 1 ? '' : ev.time
14212     //                                },
14213                                     {
14214                                       tag:'span',
14215                                       cls: 'fc-event-title',
14216                                       html : String.format('{0}', ev.title)
14217                                     }
14218
14219
14220                                 ]
14221                             },
14222                             {
14223                                 cls: 'ui-resizable-handle ui-resizable-e',
14224                                 html : '&nbsp;&nbsp;&nbsp'
14225                             }
14226
14227                         ]
14228                     };
14229
14230                     if (i == 0) {
14231                         cfg.cls += ' fc-event-start';
14232                     }
14233                     if ((i+1) == rows.length) {
14234                         cfg.cls += ' fc-event-end';
14235                     }
14236
14237                     var ctr = _this.el.select('.fc-event-container',true).first();
14238                     var cg = ctr.createChild(cfg);
14239
14240                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14241                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14242
14243                     var r = (c.more.length) ? 1 : 0;
14244                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14245                     cg.setWidth(ebox.right - sbox.x -2);
14246
14247                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14248                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14249                     cg.on('click', _this.onEventClick, _this, ev);
14250
14251                     ev.els.push(cg);
14252                     
14253                 }
14254                 
14255             }
14256             
14257             
14258             if(c.more.length){
14259                 var  cfg = {
14260                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14261                     style : 'position: absolute',
14262                     unselectable : "on",
14263                     cn : [
14264                         {
14265                             cls: 'fc-event-inner',
14266                             cn : [
14267                                 {
14268                                   tag:'span',
14269                                   cls: 'fc-event-title',
14270                                   html : 'More'
14271                                 }
14272
14273
14274                             ]
14275                         },
14276                         {
14277                             cls: 'ui-resizable-handle ui-resizable-e',
14278                             html : '&nbsp;&nbsp;&nbsp'
14279                         }
14280
14281                     ]
14282                 };
14283
14284                 var ctr = _this.el.select('.fc-event-container',true).first();
14285                 var cg = ctr.createChild(cfg);
14286
14287                 var sbox = c.select('.fc-day-content',true).first().getBox();
14288                 var ebox = c.select('.fc-day-content',true).first().getBox();
14289                 //Roo.log(cg);
14290                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14291                 cg.setWidth(ebox.right - sbox.x -2);
14292
14293                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14294                 
14295             }
14296             
14297         });
14298         
14299         
14300         
14301     },
14302     
14303     onEventEnter: function (e, el,event,d) {
14304         this.fireEvent('evententer', this, el, event);
14305     },
14306     
14307     onEventLeave: function (e, el,event,d) {
14308         this.fireEvent('eventleave', this, el, event);
14309     },
14310     
14311     onEventClick: function (e, el,event,d) {
14312         this.fireEvent('eventclick', this, el, event);
14313     },
14314     
14315     onMonthChange: function () {
14316         this.store.load();
14317     },
14318     
14319     onMoreEventClick: function(e, el, more)
14320     {
14321         var _this = this;
14322         
14323         this.calpopover.placement = 'right';
14324         this.calpopover.setTitle('More');
14325         
14326         this.calpopover.setContent('');
14327         
14328         var ctr = this.calpopover.el.select('.popover-content', true).first();
14329         
14330         Roo.each(more, function(m){
14331             var cfg = {
14332                 cls : 'fc-event-hori fc-event-draggable',
14333                 html : m.title
14334             }
14335             var cg = ctr.createChild(cfg);
14336             
14337             cg.on('click', _this.onEventClick, _this, m);
14338         });
14339         
14340         this.calpopover.show(el);
14341         
14342         
14343     },
14344     
14345     onLoad: function () 
14346     {   
14347         this.calevents = [];
14348         var cal = this;
14349         
14350         if(this.store.getCount() > 0){
14351             this.store.data.each(function(d){
14352                cal.addItem({
14353                     id : d.data.id,
14354                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14355                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14356                     time : d.data.start_time,
14357                     title : d.data.title,
14358                     description : d.data.description,
14359                     venue : d.data.venue
14360                 });
14361             });
14362         }
14363         
14364         this.renderEvents();
14365         
14366         if(this.calevents.length && this.loadMask){
14367             this.maskEl.hide();
14368         }
14369     },
14370     
14371     onBeforeLoad: function()
14372     {
14373         this.clearEvents();
14374         if(this.loadMask){
14375             this.maskEl.show();
14376         }
14377     }
14378 });
14379
14380  
14381  /*
14382  * - LGPL
14383  *
14384  * element
14385  * 
14386  */
14387
14388 /**
14389  * @class Roo.bootstrap.Popover
14390  * @extends Roo.bootstrap.Component
14391  * Bootstrap Popover class
14392  * @cfg {String} html contents of the popover   (or false to use children..)
14393  * @cfg {String} title of popover (or false to hide)
14394  * @cfg {String} placement how it is placed
14395  * @cfg {String} trigger click || hover (or false to trigger manually)
14396  * @cfg {String} over what (parent or false to trigger manually.)
14397  * @cfg {Number} delay - delay before showing
14398  
14399  * @constructor
14400  * Create a new Popover
14401  * @param {Object} config The config object
14402  */
14403
14404 Roo.bootstrap.Popover = function(config){
14405     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14406 };
14407
14408 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14409     
14410     title: 'Fill in a title',
14411     html: false,
14412     
14413     placement : 'right',
14414     trigger : 'hover', // hover
14415     
14416     delay : 0,
14417     
14418     over: 'parent',
14419     
14420     can_build_overlaid : false,
14421     
14422     getChildContainer : function()
14423     {
14424         return this.el.select('.popover-content',true).first();
14425     },
14426     
14427     getAutoCreate : function(){
14428          Roo.log('make popover?');
14429         var cfg = {
14430            cls : 'popover roo-dynamic',
14431            style: 'display:block',
14432            cn : [
14433                 {
14434                     cls : 'arrow'
14435                 },
14436                 {
14437                     cls : 'popover-inner',
14438                     cn : [
14439                         {
14440                             tag: 'h3',
14441                             cls: 'popover-title',
14442                             html : this.title
14443                         },
14444                         {
14445                             cls : 'popover-content',
14446                             html : this.html
14447                         }
14448                     ]
14449                     
14450                 }
14451            ]
14452         };
14453         
14454         return cfg;
14455     },
14456     setTitle: function(str)
14457     {
14458         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14459     },
14460     setContent: function(str)
14461     {
14462         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14463     },
14464     // as it get's added to the bottom of the page.
14465     onRender : function(ct, position)
14466     {
14467         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14468         if(!this.el){
14469             var cfg = Roo.apply({},  this.getAutoCreate());
14470             cfg.id = Roo.id();
14471             
14472             if (this.cls) {
14473                 cfg.cls += ' ' + this.cls;
14474             }
14475             if (this.style) {
14476                 cfg.style = this.style;
14477             }
14478             Roo.log("adding to ")
14479             this.el = Roo.get(document.body).createChild(cfg, position);
14480             Roo.log(this.el);
14481         }
14482         this.initEvents();
14483     },
14484     
14485     initEvents : function()
14486     {
14487         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14488         this.el.enableDisplayMode('block');
14489         this.el.hide();
14490         if (this.over === false) {
14491             return; 
14492         }
14493         if (this.triggers === false) {
14494             return;
14495         }
14496         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14497         var triggers = this.trigger ? this.trigger.split(' ') : [];
14498         Roo.each(triggers, function(trigger) {
14499         
14500             if (trigger == 'click') {
14501                 on_el.on('click', this.toggle, this);
14502             } else if (trigger != 'manual') {
14503                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14504                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14505       
14506                 on_el.on(eventIn  ,this.enter, this);
14507                 on_el.on(eventOut, this.leave, this);
14508             }
14509         }, this);
14510         
14511     },
14512     
14513     
14514     // private
14515     timeout : null,
14516     hoverState : null,
14517     
14518     toggle : function () {
14519         this.hoverState == 'in' ? this.leave() : this.enter();
14520     },
14521     
14522     enter : function () {
14523        
14524     
14525         clearTimeout(this.timeout);
14526     
14527         this.hoverState = 'in';
14528     
14529         if (!this.delay || !this.delay.show) {
14530             this.show();
14531             return;
14532         }
14533         var _t = this;
14534         this.timeout = setTimeout(function () {
14535             if (_t.hoverState == 'in') {
14536                 _t.show();
14537             }
14538         }, this.delay.show)
14539     },
14540     leave : function() {
14541         clearTimeout(this.timeout);
14542     
14543         this.hoverState = 'out';
14544     
14545         if (!this.delay || !this.delay.hide) {
14546             this.hide();
14547             return;
14548         }
14549         var _t = this;
14550         this.timeout = setTimeout(function () {
14551             if (_t.hoverState == 'out') {
14552                 _t.hide();
14553             }
14554         }, this.delay.hide)
14555     },
14556     
14557     show : function (on_el)
14558     {
14559         if (!on_el) {
14560             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14561         }
14562         // set content.
14563         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14564         if (this.html !== false) {
14565             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14566         }
14567         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14568         if (!this.title.length) {
14569             this.el.select('.popover-title',true).hide();
14570         }
14571         
14572         var placement = typeof this.placement == 'function' ?
14573             this.placement.call(this, this.el, on_el) :
14574             this.placement;
14575             
14576         var autoToken = /\s?auto?\s?/i;
14577         var autoPlace = autoToken.test(placement);
14578         if (autoPlace) {
14579             placement = placement.replace(autoToken, '') || 'top';
14580         }
14581         
14582         //this.el.detach()
14583         //this.el.setXY([0,0]);
14584         this.el.show();
14585         this.el.dom.style.display='block';
14586         this.el.addClass(placement);
14587         
14588         //this.el.appendTo(on_el);
14589         
14590         var p = this.getPosition();
14591         var box = this.el.getBox();
14592         
14593         if (autoPlace) {
14594             // fixme..
14595         }
14596         var align = Roo.bootstrap.Popover.alignment[placement];
14597         this.el.alignTo(on_el, align[0],align[1]);
14598         //var arrow = this.el.select('.arrow',true).first();
14599         //arrow.set(align[2], 
14600         
14601         this.el.addClass('in');
14602         this.hoverState = null;
14603         
14604         if (this.el.hasClass('fade')) {
14605             // fade it?
14606         }
14607         
14608     },
14609     hide : function()
14610     {
14611         this.el.setXY([0,0]);
14612         this.el.removeClass('in');
14613         this.el.hide();
14614         
14615     }
14616     
14617 });
14618
14619 Roo.bootstrap.Popover.alignment = {
14620     'left' : ['r-l', [-10,0], 'right'],
14621     'right' : ['l-r', [10,0], 'left'],
14622     'bottom' : ['t-b', [0,10], 'top'],
14623     'top' : [ 'b-t', [0,-10], 'bottom']
14624 };
14625
14626  /*
14627  * - LGPL
14628  *
14629  * Progress
14630  * 
14631  */
14632
14633 /**
14634  * @class Roo.bootstrap.Progress
14635  * @extends Roo.bootstrap.Component
14636  * Bootstrap Progress class
14637  * @cfg {Boolean} striped striped of the progress bar
14638  * @cfg {Boolean} active animated of the progress bar
14639  * 
14640  * 
14641  * @constructor
14642  * Create a new Progress
14643  * @param {Object} config The config object
14644  */
14645
14646 Roo.bootstrap.Progress = function(config){
14647     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14648 };
14649
14650 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14651     
14652     striped : false,
14653     active: false,
14654     
14655     getAutoCreate : function(){
14656         var cfg = {
14657             tag: 'div',
14658             cls: 'progress'
14659         };
14660         
14661         
14662         if(this.striped){
14663             cfg.cls += ' progress-striped';
14664         }
14665       
14666         if(this.active){
14667             cfg.cls += ' active';
14668         }
14669         
14670         
14671         return cfg;
14672     }
14673    
14674 });
14675
14676  
14677
14678  /*
14679  * - LGPL
14680  *
14681  * ProgressBar
14682  * 
14683  */
14684
14685 /**
14686  * @class Roo.bootstrap.ProgressBar
14687  * @extends Roo.bootstrap.Component
14688  * Bootstrap ProgressBar class
14689  * @cfg {Number} aria_valuenow aria-value now
14690  * @cfg {Number} aria_valuemin aria-value min
14691  * @cfg {Number} aria_valuemax aria-value max
14692  * @cfg {String} label label for the progress bar
14693  * @cfg {String} panel (success | info | warning | danger )
14694  * @cfg {String} role role of the progress bar
14695  * @cfg {String} sr_only text
14696  * 
14697  * 
14698  * @constructor
14699  * Create a new ProgressBar
14700  * @param {Object} config The config object
14701  */
14702
14703 Roo.bootstrap.ProgressBar = function(config){
14704     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14705 };
14706
14707 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14708     
14709     aria_valuenow : 0,
14710     aria_valuemin : 0,
14711     aria_valuemax : 100,
14712     label : false,
14713     panel : false,
14714     role : false,
14715     sr_only: false,
14716     
14717     getAutoCreate : function()
14718     {
14719         
14720         var cfg = {
14721             tag: 'div',
14722             cls: 'progress-bar',
14723             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14724         };
14725         
14726         if(this.sr_only){
14727             cfg.cn = {
14728                 tag: 'span',
14729                 cls: 'sr-only',
14730                 html: this.sr_only
14731             }
14732         }
14733         
14734         if(this.role){
14735             cfg.role = this.role;
14736         }
14737         
14738         if(this.aria_valuenow){
14739             cfg['aria-valuenow'] = this.aria_valuenow;
14740         }
14741         
14742         if(this.aria_valuemin){
14743             cfg['aria-valuemin'] = this.aria_valuemin;
14744         }
14745         
14746         if(this.aria_valuemax){
14747             cfg['aria-valuemax'] = this.aria_valuemax;
14748         }
14749         
14750         if(this.label && !this.sr_only){
14751             cfg.html = this.label;
14752         }
14753         
14754         if(this.panel){
14755             cfg.cls += ' progress-bar-' + this.panel;
14756         }
14757         
14758         return cfg;
14759     },
14760     
14761     update : function(aria_valuenow)
14762     {
14763         this.aria_valuenow = aria_valuenow;
14764         
14765         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14766     }
14767    
14768 });
14769
14770  
14771
14772  /*
14773  * - LGPL
14774  *
14775  * column
14776  * 
14777  */
14778
14779 /**
14780  * @class Roo.bootstrap.TabGroup
14781  * @extends Roo.bootstrap.Column
14782  * Bootstrap Column class
14783  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14784  * @cfg {Boolean} carousel true to make the group behave like a carousel
14785  * @cfg {Number} bullets show the panel pointer.. default 0
14786  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14787  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14788  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14789  * 
14790  * @constructor
14791  * Create a new TabGroup
14792  * @param {Object} config The config object
14793  */
14794
14795 Roo.bootstrap.TabGroup = function(config){
14796     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14797     if (!this.navId) {
14798         this.navId = Roo.id();
14799     }
14800     this.tabs = [];
14801     Roo.bootstrap.TabGroup.register(this);
14802     
14803 };
14804
14805 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14806     
14807     carousel : false,
14808     transition : false,
14809     bullets : 0,
14810     timer : 0,
14811     autoslide : false,
14812     slideFn : false,
14813     slideOnTouch : false,
14814     
14815     getAutoCreate : function()
14816     {
14817         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14818         
14819         cfg.cls += ' tab-content';
14820         
14821         Roo.log('get auto create...............');
14822         
14823         if (this.carousel) {
14824             cfg.cls += ' carousel slide';
14825             
14826             cfg.cn = [{
14827                cls : 'carousel-inner'
14828             }];
14829         
14830             if(this.bullets > 0 && !Roo.isTouch){
14831                 
14832                 var bullets = {
14833                     cls : 'carousel-bullets',
14834                     cn : []
14835                 };
14836                 
14837                 if(this.bullets_cls){
14838                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14839                 }
14840                 
14841                 for (var i = 0; i < this.bullets; i++){
14842                     bullets.cn.push({
14843                         cls : 'bullet bullet-' + i
14844                     });
14845                 }
14846                 
14847                 bullets.cn.push({
14848                     cls : 'clear'
14849                 });
14850                 
14851                 cfg.cn[0].cn = bullets;
14852             }
14853         }
14854         
14855         return cfg;
14856     },
14857     
14858     initEvents:  function()
14859     {
14860         Roo.log('-------- init events on tab group ---------');
14861         
14862         if(this.bullets > 0 && !Roo.isTouch){
14863             this.initBullet();
14864         }
14865         
14866         Roo.log(this);
14867         
14868         if(Roo.isTouch && this.slideOnTouch){
14869             this.el.on("touchstart", this.onTouchStart, this);
14870         }
14871         
14872         if(this.autoslide){
14873             var _this = this;
14874             
14875             this.slideFn = window.setInterval(function() {
14876                 _this.showPanelNext();
14877             }, this.timer);
14878         }
14879         
14880     },
14881     
14882     onTouchStart : function(e, el, o)
14883     {
14884         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14885             return;
14886         }
14887         
14888         this.showPanelNext();
14889     },
14890     
14891     getChildContainer : function()
14892     {
14893         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14894     },
14895     
14896     /**
14897     * register a Navigation item
14898     * @param {Roo.bootstrap.NavItem} the navitem to add
14899     */
14900     register : function(item)
14901     {
14902         this.tabs.push( item);
14903         item.navId = this.navId; // not really needed..
14904     
14905     },
14906     
14907     getActivePanel : function()
14908     {
14909         var r = false;
14910         Roo.each(this.tabs, function(t) {
14911             if (t.active) {
14912                 r = t;
14913                 return false;
14914             }
14915             return null;
14916         });
14917         return r;
14918         
14919     },
14920     getPanelByName : function(n)
14921     {
14922         var r = false;
14923         Roo.each(this.tabs, function(t) {
14924             if (t.tabId == n) {
14925                 r = t;
14926                 return false;
14927             }
14928             return null;
14929         });
14930         return r;
14931     },
14932     indexOfPanel : function(p)
14933     {
14934         var r = false;
14935         Roo.each(this.tabs, function(t,i) {
14936             if (t.tabId == p.tabId) {
14937                 r = i;
14938                 return false;
14939             }
14940             return null;
14941         });
14942         return r;
14943     },
14944     /**
14945      * show a specific panel
14946      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14947      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14948      */
14949     showPanel : function (pan)
14950     {
14951         if(this.transition){
14952             Roo.log("waiting for the transitionend");
14953             return;
14954         }
14955         
14956         if (typeof(pan) == 'number') {
14957             pan = this.tabs[pan];
14958         }
14959         if (typeof(pan) == 'string') {
14960             pan = this.getPanelByName(pan);
14961         }
14962         if (pan.tabId == this.getActivePanel().tabId) {
14963             return true;
14964         }
14965         var cur = this.getActivePanel();
14966         
14967         if (false === cur.fireEvent('beforedeactivate')) {
14968             return false;
14969         }
14970         
14971         if(this.bullets > 0 && !Roo.isTouch){
14972             this.setActiveBullet(this.indexOfPanel(pan));
14973         }
14974         
14975         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14976             
14977             this.transition = true;
14978             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14979             var lr = dir == 'next' ? 'left' : 'right';
14980             pan.el.addClass(dir); // or prev
14981             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14982             cur.el.addClass(lr); // or right
14983             pan.el.addClass(lr);
14984             
14985             var _this = this;
14986             cur.el.on('transitionend', function() {
14987                 Roo.log("trans end?");
14988                 
14989                 pan.el.removeClass([lr,dir]);
14990                 pan.setActive(true);
14991                 
14992                 cur.el.removeClass([lr]);
14993                 cur.setActive(false);
14994                 
14995                 _this.transition = false;
14996                 
14997             }, this, { single:  true } );
14998             
14999             return true;
15000         }
15001         
15002         cur.setActive(false);
15003         pan.setActive(true);
15004         
15005         return true;
15006         
15007     },
15008     showPanelNext : function()
15009     {
15010         var i = this.indexOfPanel(this.getActivePanel());
15011         
15012         if (i >= this.tabs.length - 1 && !this.autoslide) {
15013             return;
15014         }
15015         
15016         if (i >= this.tabs.length - 1 && this.autoslide) {
15017             i = -1;
15018         }
15019         
15020         this.showPanel(this.tabs[i+1]);
15021     },
15022     
15023     showPanelPrev : function()
15024     {
15025         var i = this.indexOfPanel(this.getActivePanel());
15026         
15027         if (i  < 1 && !this.autoslide) {
15028             return;
15029         }
15030         
15031         if (i < 1 && this.autoslide) {
15032             i = this.tabs.length;
15033         }
15034         
15035         this.showPanel(this.tabs[i-1]);
15036     },
15037     
15038     initBullet : function()
15039     {
15040         if(Roo.isTouch){
15041             return;
15042         }
15043         
15044         var _this = this;
15045         
15046         for (var i = 0; i < this.bullets; i++){
15047             var bullet = this.el.select('.bullet-' + i, true).first();
15048
15049             if(!bullet){
15050                 continue;
15051             }
15052
15053             bullet.on('click', (function(e, el, o, ii, t){
15054
15055                 e.preventDefault();
15056
15057                 _this.showPanel(ii);
15058
15059                 if(_this.autoslide && _this.slideFn){
15060                     clearInterval(_this.slideFn);
15061                     _this.slideFn = window.setInterval(function() {
15062                         _this.showPanelNext();
15063                     }, _this.timer);
15064                 }
15065
15066             }).createDelegate(this, [i, bullet], true));
15067         }
15068     },
15069     
15070     setActiveBullet : function(i)
15071     {
15072         if(Roo.isTouch){
15073             return;
15074         }
15075         
15076         Roo.each(this.el.select('.bullet', true).elements, function(el){
15077             el.removeClass('selected');
15078         });
15079
15080         var bullet = this.el.select('.bullet-' + i, true).first();
15081         
15082         if(!bullet){
15083             return;
15084         }
15085         
15086         bullet.addClass('selected');
15087     }
15088     
15089     
15090   
15091 });
15092
15093  
15094
15095  
15096  
15097 Roo.apply(Roo.bootstrap.TabGroup, {
15098     
15099     groups: {},
15100      /**
15101     * register a Navigation Group
15102     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15103     */
15104     register : function(navgrp)
15105     {
15106         this.groups[navgrp.navId] = navgrp;
15107         
15108     },
15109     /**
15110     * fetch a Navigation Group based on the navigation ID
15111     * if one does not exist , it will get created.
15112     * @param {string} the navgroup to add
15113     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15114     */
15115     get: function(navId) {
15116         if (typeof(this.groups[navId]) == 'undefined') {
15117             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15118         }
15119         return this.groups[navId] ;
15120     }
15121     
15122     
15123     
15124 });
15125
15126  /*
15127  * - LGPL
15128  *
15129  * TabPanel
15130  * 
15131  */
15132
15133 /**
15134  * @class Roo.bootstrap.TabPanel
15135  * @extends Roo.bootstrap.Component
15136  * Bootstrap TabPanel class
15137  * @cfg {Boolean} active panel active
15138  * @cfg {String} html panel content
15139  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15140  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15141  * 
15142  * 
15143  * @constructor
15144  * Create a new TabPanel
15145  * @param {Object} config The config object
15146  */
15147
15148 Roo.bootstrap.TabPanel = function(config){
15149     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15150     this.addEvents({
15151         /**
15152              * @event changed
15153              * Fires when the active status changes
15154              * @param {Roo.bootstrap.TabPanel} this
15155              * @param {Boolean} state the new state
15156             
15157          */
15158         'changed': true,
15159         /**
15160              * @event beforedeactivate
15161              * Fires before a tab is de-activated - can be used to do validation on a form.
15162              * @param {Roo.bootstrap.TabPanel} this
15163              * @return {Boolean} false if there is an error
15164             
15165          */
15166         'beforedeactivate': true
15167      });
15168     
15169     this.tabId = this.tabId || Roo.id();
15170   
15171 };
15172
15173 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15174     
15175     active: false,
15176     html: false,
15177     tabId: false,
15178     navId : false,
15179     
15180     getAutoCreate : function(){
15181         var cfg = {
15182             tag: 'div',
15183             // item is needed for carousel - not sure if it has any effect otherwise
15184             cls: 'tab-pane item',
15185             html: this.html || ''
15186         };
15187         
15188         if(this.active){
15189             cfg.cls += ' active';
15190         }
15191         
15192         if(this.tabId){
15193             cfg.tabId = this.tabId;
15194         }
15195         
15196         
15197         return cfg;
15198     },
15199     
15200     initEvents:  function()
15201     {
15202         Roo.log('-------- init events on tab panel ---------');
15203         
15204         var p = this.parent();
15205         this.navId = this.navId || p.navId;
15206         
15207         if (typeof(this.navId) != 'undefined') {
15208             // not really needed.. but just in case.. parent should be a NavGroup.
15209             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15210             Roo.log(['register', tg, this]);
15211             tg.register(this);
15212             
15213             var i = tg.tabs.length - 1;
15214             
15215             if(this.active && tg.bullets > 0 && i < tg.bullets){
15216                 tg.setActiveBullet(i);
15217             }
15218         }
15219         
15220     },
15221     
15222     
15223     onRender : function(ct, position)
15224     {
15225        // Roo.log("Call onRender: " + this.xtype);
15226         
15227         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15228         
15229         
15230         
15231         
15232         
15233     },
15234     
15235     setActive: function(state)
15236     {
15237         Roo.log("panel - set active " + this.tabId + "=" + state);
15238         
15239         this.active = state;
15240         if (!state) {
15241             this.el.removeClass('active');
15242             
15243         } else  if (!this.el.hasClass('active')) {
15244             this.el.addClass('active');
15245         }
15246         
15247         this.fireEvent('changed', this, state);
15248     }
15249     
15250     
15251 });
15252  
15253
15254  
15255
15256  /*
15257  * - LGPL
15258  *
15259  * DateField
15260  * 
15261  */
15262
15263 /**
15264  * @class Roo.bootstrap.DateField
15265  * @extends Roo.bootstrap.Input
15266  * Bootstrap DateField class
15267  * @cfg {Number} weekStart default 0
15268  * @cfg {String} viewMode default empty, (months|years)
15269  * @cfg {String} minViewMode default empty, (months|years)
15270  * @cfg {Number} startDate default -Infinity
15271  * @cfg {Number} endDate default Infinity
15272  * @cfg {Boolean} todayHighlight default false
15273  * @cfg {Boolean} todayBtn default false
15274  * @cfg {Boolean} calendarWeeks default false
15275  * @cfg {Object} daysOfWeekDisabled default empty
15276  * @cfg {Boolean} singleMode default false (true | false)
15277  * 
15278  * @cfg {Boolean} keyboardNavigation default true
15279  * @cfg {String} language default en
15280  * 
15281  * @constructor
15282  * Create a new DateField
15283  * @param {Object} config The config object
15284  */
15285
15286 Roo.bootstrap.DateField = function(config){
15287     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15288      this.addEvents({
15289             /**
15290              * @event show
15291              * Fires when this field show.
15292              * @param {Roo.bootstrap.DateField} this
15293              * @param {Mixed} date The date value
15294              */
15295             show : true,
15296             /**
15297              * @event show
15298              * Fires when this field hide.
15299              * @param {Roo.bootstrap.DateField} this
15300              * @param {Mixed} date The date value
15301              */
15302             hide : true,
15303             /**
15304              * @event select
15305              * Fires when select a date.
15306              * @param {Roo.bootstrap.DateField} this
15307              * @param {Mixed} date The date value
15308              */
15309             select : true
15310         });
15311 };
15312
15313 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15314     
15315     /**
15316      * @cfg {String} format
15317      * The default date format string which can be overriden for localization support.  The format must be
15318      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15319      */
15320     format : "m/d/y",
15321     /**
15322      * @cfg {String} altFormats
15323      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15324      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15325      */
15326     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15327     
15328     weekStart : 0,
15329     
15330     viewMode : '',
15331     
15332     minViewMode : '',
15333     
15334     todayHighlight : false,
15335     
15336     todayBtn: false,
15337     
15338     language: 'en',
15339     
15340     keyboardNavigation: true,
15341     
15342     calendarWeeks: false,
15343     
15344     startDate: -Infinity,
15345     
15346     endDate: Infinity,
15347     
15348     daysOfWeekDisabled: [],
15349     
15350     _events: [],
15351     
15352     singleMode : false,
15353     
15354     UTCDate: function()
15355     {
15356         return new Date(Date.UTC.apply(Date, arguments));
15357     },
15358     
15359     UTCToday: function()
15360     {
15361         var today = new Date();
15362         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15363     },
15364     
15365     getDate: function() {
15366             var d = this.getUTCDate();
15367             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15368     },
15369     
15370     getUTCDate: function() {
15371             return this.date;
15372     },
15373     
15374     setDate: function(d) {
15375             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15376     },
15377     
15378     setUTCDate: function(d) {
15379             this.date = d;
15380             this.setValue(this.formatDate(this.date));
15381     },
15382         
15383     onRender: function(ct, position)
15384     {
15385         
15386         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15387         
15388         this.language = this.language || 'en';
15389         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15390         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15391         
15392         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15393         this.format = this.format || 'm/d/y';
15394         this.isInline = false;
15395         this.isInput = true;
15396         this.component = this.el.select('.add-on', true).first() || false;
15397         this.component = (this.component && this.component.length === 0) ? false : this.component;
15398         this.hasInput = this.component && this.inputEL().length;
15399         
15400         if (typeof(this.minViewMode === 'string')) {
15401             switch (this.minViewMode) {
15402                 case 'months':
15403                     this.minViewMode = 1;
15404                     break;
15405                 case 'years':
15406                     this.minViewMode = 2;
15407                     break;
15408                 default:
15409                     this.minViewMode = 0;
15410                     break;
15411             }
15412         }
15413         
15414         if (typeof(this.viewMode === 'string')) {
15415             switch (this.viewMode) {
15416                 case 'months':
15417                     this.viewMode = 1;
15418                     break;
15419                 case 'years':
15420                     this.viewMode = 2;
15421                     break;
15422                 default:
15423                     this.viewMode = 0;
15424                     break;
15425             }
15426         }
15427                 
15428         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15429         
15430 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15431         
15432         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433         
15434         this.picker().on('mousedown', this.onMousedown, this);
15435         this.picker().on('click', this.onClick, this);
15436         
15437         this.picker().addClass('datepicker-dropdown');
15438         
15439         this.startViewMode = this.viewMode;
15440         
15441         if(this.singleMode){
15442             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15443                 v.setVisibilityMode(Roo.Element.DISPLAY)
15444                 v.hide();
15445             });
15446             
15447             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15448                 v.setStyle('width', '189px');
15449             });
15450         }
15451         
15452         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15453             if(!this.calendarWeeks){
15454                 v.remove();
15455                 return;
15456             }
15457             
15458             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15459             v.attr('colspan', function(i, val){
15460                 return parseInt(val) + 1;
15461             });
15462         })
15463                         
15464         
15465         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15466         
15467         this.setStartDate(this.startDate);
15468         this.setEndDate(this.endDate);
15469         
15470         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15471         
15472         this.fillDow();
15473         this.fillMonths();
15474         this.update();
15475         this.showMode();
15476         
15477         if(this.isInline) {
15478             this.show();
15479         }
15480     },
15481     
15482     picker : function()
15483     {
15484         return this.pickerEl;
15485 //        return this.el.select('.datepicker', true).first();
15486     },
15487     
15488     fillDow: function()
15489     {
15490         var dowCnt = this.weekStart;
15491         
15492         var dow = {
15493             tag: 'tr',
15494             cn: [
15495                 
15496             ]
15497         };
15498         
15499         if(this.calendarWeeks){
15500             dow.cn.push({
15501                 tag: 'th',
15502                 cls: 'cw',
15503                 html: '&nbsp;'
15504             })
15505         }
15506         
15507         while (dowCnt < this.weekStart + 7) {
15508             dow.cn.push({
15509                 tag: 'th',
15510                 cls: 'dow',
15511                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15512             });
15513         }
15514         
15515         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15516     },
15517     
15518     fillMonths: function()
15519     {    
15520         var i = 0;
15521         var months = this.picker().select('>.datepicker-months td', true).first();
15522         
15523         months.dom.innerHTML = '';
15524         
15525         while (i < 12) {
15526             var month = {
15527                 tag: 'span',
15528                 cls: 'month',
15529                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15530             }
15531             
15532             months.createChild(month);
15533         }
15534         
15535     },
15536     
15537     update: function()
15538     {
15539         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;
15540         
15541         if (this.date < this.startDate) {
15542             this.viewDate = new Date(this.startDate);
15543         } else if (this.date > this.endDate) {
15544             this.viewDate = new Date(this.endDate);
15545         } else {
15546             this.viewDate = new Date(this.date);
15547         }
15548         
15549         this.fill();
15550     },
15551     
15552     fill: function() 
15553     {
15554         var d = new Date(this.viewDate),
15555                 year = d.getUTCFullYear(),
15556                 month = d.getUTCMonth(),
15557                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15558                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15559                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15560                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15561                 currentDate = this.date && this.date.valueOf(),
15562                 today = this.UTCToday();
15563         
15564         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15565         
15566 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15567         
15568 //        this.picker.select('>tfoot th.today').
15569 //                                              .text(dates[this.language].today)
15570 //                                              .toggle(this.todayBtn !== false);
15571     
15572         this.updateNavArrows();
15573         this.fillMonths();
15574                                                 
15575         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15576         
15577         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15578          
15579         prevMonth.setUTCDate(day);
15580         
15581         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15582         
15583         var nextMonth = new Date(prevMonth);
15584         
15585         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15586         
15587         nextMonth = nextMonth.valueOf();
15588         
15589         var fillMonths = false;
15590         
15591         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15592         
15593         while(prevMonth.valueOf() < nextMonth) {
15594             var clsName = '';
15595             
15596             if (prevMonth.getUTCDay() === this.weekStart) {
15597                 if(fillMonths){
15598                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15599                 }
15600                     
15601                 fillMonths = {
15602                     tag: 'tr',
15603                     cn: []
15604                 };
15605                 
15606                 if(this.calendarWeeks){
15607                     // ISO 8601: First week contains first thursday.
15608                     // ISO also states week starts on Monday, but we can be more abstract here.
15609                     var
15610                     // Start of current week: based on weekstart/current date
15611                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15612                     // Thursday of this week
15613                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15614                     // First Thursday of year, year from thursday
15615                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15616                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15617                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15618                     
15619                     fillMonths.cn.push({
15620                         tag: 'td',
15621                         cls: 'cw',
15622                         html: calWeek
15623                     });
15624                 }
15625             }
15626             
15627             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15628                 clsName += ' old';
15629             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15630                 clsName += ' new';
15631             }
15632             if (this.todayHighlight &&
15633                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15634                 prevMonth.getUTCMonth() == today.getMonth() &&
15635                 prevMonth.getUTCDate() == today.getDate()) {
15636                 clsName += ' today';
15637             }
15638             
15639             if (currentDate && prevMonth.valueOf() === currentDate) {
15640                 clsName += ' active';
15641             }
15642             
15643             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15644                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15645                     clsName += ' disabled';
15646             }
15647             
15648             fillMonths.cn.push({
15649                 tag: 'td',
15650                 cls: 'day ' + clsName,
15651                 html: prevMonth.getDate()
15652             })
15653             
15654             prevMonth.setDate(prevMonth.getDate()+1);
15655         }
15656           
15657         var currentYear = this.date && this.date.getUTCFullYear();
15658         var currentMonth = this.date && this.date.getUTCMonth();
15659         
15660         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15661         
15662         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15663             v.removeClass('active');
15664             
15665             if(currentYear === year && k === currentMonth){
15666                 v.addClass('active');
15667             }
15668             
15669             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15670                 v.addClass('disabled');
15671             }
15672             
15673         });
15674         
15675         
15676         year = parseInt(year/10, 10) * 10;
15677         
15678         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15679         
15680         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15681         
15682         year -= 1;
15683         for (var i = -1; i < 11; i++) {
15684             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15685                 tag: 'span',
15686                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15687                 html: year
15688             })
15689             
15690             year += 1;
15691         }
15692     },
15693     
15694     showMode: function(dir) 
15695     {
15696         if (dir) {
15697             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15698         }
15699         
15700         Roo.each(this.picker().select('>div',true).elements, function(v){
15701             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15702             v.hide();
15703         });
15704         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15705     },
15706     
15707     place: function()
15708     {
15709         if(this.isInline) return;
15710         
15711         this.picker().removeClass(['bottom', 'top']);
15712         
15713         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15714             /*
15715              * place to the top of element!
15716              *
15717              */
15718             
15719             this.picker().addClass('top');
15720             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15721             
15722             return;
15723         }
15724         
15725         this.picker().addClass('bottom');
15726         
15727         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15728     },
15729     
15730     parseDate : function(value)
15731     {
15732         if(!value || value instanceof Date){
15733             return value;
15734         }
15735         var v = Date.parseDate(value, this.format);
15736         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15737             v = Date.parseDate(value, 'Y-m-d');
15738         }
15739         if(!v && this.altFormats){
15740             if(!this.altFormatsArray){
15741                 this.altFormatsArray = this.altFormats.split("|");
15742             }
15743             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15744                 v = Date.parseDate(value, this.altFormatsArray[i]);
15745             }
15746         }
15747         return v;
15748     },
15749     
15750     formatDate : function(date, fmt)
15751     {   
15752         return (!date || !(date instanceof Date)) ?
15753         date : date.dateFormat(fmt || this.format);
15754     },
15755     
15756     onFocus : function()
15757     {
15758         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15759         this.show();
15760     },
15761     
15762     onBlur : function()
15763     {
15764         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15765         
15766         var d = this.inputEl().getValue();
15767         
15768         this.setValue(d);
15769                 
15770         this.hide();
15771     },
15772     
15773     show : function()
15774     {
15775         this.picker().show();
15776         this.update();
15777         this.place();
15778         
15779         this.fireEvent('show', this, this.date);
15780     },
15781     
15782     hide : function()
15783     {
15784         if(this.isInline) return;
15785         this.picker().hide();
15786         this.viewMode = this.startViewMode;
15787         this.showMode();
15788         
15789         this.fireEvent('hide', this, this.date);
15790         
15791     },
15792     
15793     onMousedown: function(e)
15794     {
15795         e.stopPropagation();
15796         e.preventDefault();
15797     },
15798     
15799     keyup: function(e)
15800     {
15801         Roo.bootstrap.DateField.superclass.keyup.call(this);
15802         this.update();
15803     },
15804
15805     setValue: function(v)
15806     {
15807         
15808         // v can be a string or a date..
15809         
15810         
15811         var d = new Date(this.parseDate(v) ).clearTime();
15812         
15813         if(isNaN(d.getTime())){
15814             this.date = this.viewDate = '';
15815             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15816             return;
15817         }
15818         
15819         v = this.formatDate(d);
15820         
15821         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15822         
15823         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15824      
15825         this.update();
15826
15827         this.fireEvent('select', this, this.date);
15828         
15829     },
15830     
15831     getValue: function()
15832     {
15833         return this.formatDate(this.date);
15834     },
15835     
15836     fireKey: function(e)
15837     {
15838         if (!this.picker().isVisible()){
15839             if (e.keyCode == 27) // allow escape to hide and re-show picker
15840                 this.show();
15841             return;
15842         }
15843         
15844         var dateChanged = false,
15845         dir, day, month,
15846         newDate, newViewDate;
15847         
15848         switch(e.keyCode){
15849             case 27: // escape
15850                 this.hide();
15851                 e.preventDefault();
15852                 break;
15853             case 37: // left
15854             case 39: // right
15855                 if (!this.keyboardNavigation) break;
15856                 dir = e.keyCode == 37 ? -1 : 1;
15857                 
15858                 if (e.ctrlKey){
15859                     newDate = this.moveYear(this.date, dir);
15860                     newViewDate = this.moveYear(this.viewDate, dir);
15861                 } else if (e.shiftKey){
15862                     newDate = this.moveMonth(this.date, dir);
15863                     newViewDate = this.moveMonth(this.viewDate, dir);
15864                 } else {
15865                     newDate = new Date(this.date);
15866                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15867                     newViewDate = new Date(this.viewDate);
15868                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15869                 }
15870                 if (this.dateWithinRange(newDate)){
15871                     this.date = newDate;
15872                     this.viewDate = newViewDate;
15873                     this.setValue(this.formatDate(this.date));
15874 //                    this.update();
15875                     e.preventDefault();
15876                     dateChanged = true;
15877                 }
15878                 break;
15879             case 38: // up
15880             case 40: // down
15881                 if (!this.keyboardNavigation) break;
15882                 dir = e.keyCode == 38 ? -1 : 1;
15883                 if (e.ctrlKey){
15884                     newDate = this.moveYear(this.date, dir);
15885                     newViewDate = this.moveYear(this.viewDate, dir);
15886                 } else if (e.shiftKey){
15887                     newDate = this.moveMonth(this.date, dir);
15888                     newViewDate = this.moveMonth(this.viewDate, dir);
15889                 } else {
15890                     newDate = new Date(this.date);
15891                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15892                     newViewDate = new Date(this.viewDate);
15893                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15894                 }
15895                 if (this.dateWithinRange(newDate)){
15896                     this.date = newDate;
15897                     this.viewDate = newViewDate;
15898                     this.setValue(this.formatDate(this.date));
15899 //                    this.update();
15900                     e.preventDefault();
15901                     dateChanged = true;
15902                 }
15903                 break;
15904             case 13: // enter
15905                 this.setValue(this.formatDate(this.date));
15906                 this.hide();
15907                 e.preventDefault();
15908                 break;
15909             case 9: // tab
15910                 this.setValue(this.formatDate(this.date));
15911                 this.hide();
15912                 break;
15913             case 16: // shift
15914             case 17: // ctrl
15915             case 18: // alt
15916                 break;
15917             default :
15918                 this.hide();
15919                 
15920         }
15921     },
15922     
15923     
15924     onClick: function(e) 
15925     {
15926         e.stopPropagation();
15927         e.preventDefault();
15928         
15929         var target = e.getTarget();
15930         
15931         if(target.nodeName.toLowerCase() === 'i'){
15932             target = Roo.get(target).dom.parentNode;
15933         }
15934         
15935         var nodeName = target.nodeName;
15936         var className = target.className;
15937         var html = target.innerHTML;
15938         //Roo.log(nodeName);
15939         
15940         switch(nodeName.toLowerCase()) {
15941             case 'th':
15942                 switch(className) {
15943                     case 'switch':
15944                         this.showMode(1);
15945                         break;
15946                     case 'prev':
15947                     case 'next':
15948                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15949                         switch(this.viewMode){
15950                                 case 0:
15951                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15952                                         break;
15953                                 case 1:
15954                                 case 2:
15955                                         this.viewDate = this.moveYear(this.viewDate, dir);
15956                                         break;
15957                         }
15958                         this.fill();
15959                         break;
15960                     case 'today':
15961                         var date = new Date();
15962                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15963 //                        this.fill()
15964                         this.setValue(this.formatDate(this.date));
15965                         
15966                         this.hide();
15967                         break;
15968                 }
15969                 break;
15970             case 'span':
15971                 if (className.indexOf('disabled') < 0) {
15972                     this.viewDate.setUTCDate(1);
15973                     if (className.indexOf('month') > -1) {
15974                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15975                     } else {
15976                         var year = parseInt(html, 10) || 0;
15977                         this.viewDate.setUTCFullYear(year);
15978                         
15979                     }
15980                     
15981                     if(this.singleMode){
15982                         this.setValue(this.formatDate(this.viewDate));
15983                         this.hide();
15984                         return;
15985                     }
15986                     
15987                     this.showMode(-1);
15988                     this.fill();
15989                 }
15990                 break;
15991                 
15992             case 'td':
15993                 //Roo.log(className);
15994                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15995                     var day = parseInt(html, 10) || 1;
15996                     var year = this.viewDate.getUTCFullYear(),
15997                         month = this.viewDate.getUTCMonth();
15998
15999                     if (className.indexOf('old') > -1) {
16000                         if(month === 0 ){
16001                             month = 11;
16002                             year -= 1;
16003                         }else{
16004                             month -= 1;
16005                         }
16006                     } else if (className.indexOf('new') > -1) {
16007                         if (month == 11) {
16008                             month = 0;
16009                             year += 1;
16010                         } else {
16011                             month += 1;
16012                         }
16013                     }
16014                     //Roo.log([year,month,day]);
16015                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16016                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16017 //                    this.fill();
16018                     //Roo.log(this.formatDate(this.date));
16019                     this.setValue(this.formatDate(this.date));
16020                     this.hide();
16021                 }
16022                 break;
16023         }
16024     },
16025     
16026     setStartDate: function(startDate)
16027     {
16028         this.startDate = startDate || -Infinity;
16029         if (this.startDate !== -Infinity) {
16030             this.startDate = this.parseDate(this.startDate);
16031         }
16032         this.update();
16033         this.updateNavArrows();
16034     },
16035
16036     setEndDate: function(endDate)
16037     {
16038         this.endDate = endDate || Infinity;
16039         if (this.endDate !== Infinity) {
16040             this.endDate = this.parseDate(this.endDate);
16041         }
16042         this.update();
16043         this.updateNavArrows();
16044     },
16045     
16046     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16047     {
16048         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16049         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16050             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16051         }
16052         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16053             return parseInt(d, 10);
16054         });
16055         this.update();
16056         this.updateNavArrows();
16057     },
16058     
16059     updateNavArrows: function() 
16060     {
16061         if(this.singleMode){
16062             return;
16063         }
16064         
16065         var d = new Date(this.viewDate),
16066         year = d.getUTCFullYear(),
16067         month = d.getUTCMonth();
16068         
16069         Roo.each(this.picker().select('.prev', true).elements, function(v){
16070             v.show();
16071             switch (this.viewMode) {
16072                 case 0:
16073
16074                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16075                         v.hide();
16076                     }
16077                     break;
16078                 case 1:
16079                 case 2:
16080                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16081                         v.hide();
16082                     }
16083                     break;
16084             }
16085         });
16086         
16087         Roo.each(this.picker().select('.next', true).elements, function(v){
16088             v.show();
16089             switch (this.viewMode) {
16090                 case 0:
16091
16092                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16093                         v.hide();
16094                     }
16095                     break;
16096                 case 1:
16097                 case 2:
16098                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16099                         v.hide();
16100                     }
16101                     break;
16102             }
16103         })
16104     },
16105     
16106     moveMonth: function(date, dir)
16107     {
16108         if (!dir) return date;
16109         var new_date = new Date(date.valueOf()),
16110         day = new_date.getUTCDate(),
16111         month = new_date.getUTCMonth(),
16112         mag = Math.abs(dir),
16113         new_month, test;
16114         dir = dir > 0 ? 1 : -1;
16115         if (mag == 1){
16116             test = dir == -1
16117             // If going back one month, make sure month is not current month
16118             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16119             ? function(){
16120                 return new_date.getUTCMonth() == month;
16121             }
16122             // If going forward one month, make sure month is as expected
16123             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16124             : function(){
16125                 return new_date.getUTCMonth() != new_month;
16126             };
16127             new_month = month + dir;
16128             new_date.setUTCMonth(new_month);
16129             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16130             if (new_month < 0 || new_month > 11)
16131                 new_month = (new_month + 12) % 12;
16132         } else {
16133             // For magnitudes >1, move one month at a time...
16134             for (var i=0; i<mag; i++)
16135                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16136                 new_date = this.moveMonth(new_date, dir);
16137             // ...then reset the day, keeping it in the new month
16138             new_month = new_date.getUTCMonth();
16139             new_date.setUTCDate(day);
16140             test = function(){
16141                 return new_month != new_date.getUTCMonth();
16142             };
16143         }
16144         // Common date-resetting loop -- if date is beyond end of month, make it
16145         // end of month
16146         while (test()){
16147             new_date.setUTCDate(--day);
16148             new_date.setUTCMonth(new_month);
16149         }
16150         return new_date;
16151     },
16152
16153     moveYear: function(date, dir)
16154     {
16155         return this.moveMonth(date, dir*12);
16156     },
16157
16158     dateWithinRange: function(date)
16159     {
16160         return date >= this.startDate && date <= this.endDate;
16161     },
16162
16163     
16164     remove: function() 
16165     {
16166         this.picker().remove();
16167     }
16168    
16169 });
16170
16171 Roo.apply(Roo.bootstrap.DateField,  {
16172     
16173     head : {
16174         tag: 'thead',
16175         cn: [
16176         {
16177             tag: 'tr',
16178             cn: [
16179             {
16180                 tag: 'th',
16181                 cls: 'prev',
16182                 html: '<i class="fa fa-arrow-left"/>'
16183             },
16184             {
16185                 tag: 'th',
16186                 cls: 'switch',
16187                 colspan: '5'
16188             },
16189             {
16190                 tag: 'th',
16191                 cls: 'next',
16192                 html: '<i class="fa fa-arrow-right"/>'
16193             }
16194
16195             ]
16196         }
16197         ]
16198     },
16199     
16200     content : {
16201         tag: 'tbody',
16202         cn: [
16203         {
16204             tag: 'tr',
16205             cn: [
16206             {
16207                 tag: 'td',
16208                 colspan: '7'
16209             }
16210             ]
16211         }
16212         ]
16213     },
16214     
16215     footer : {
16216         tag: 'tfoot',
16217         cn: [
16218         {
16219             tag: 'tr',
16220             cn: [
16221             {
16222                 tag: 'th',
16223                 colspan: '7',
16224                 cls: 'today'
16225             }
16226                     
16227             ]
16228         }
16229         ]
16230     },
16231     
16232     dates:{
16233         en: {
16234             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16235             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16236             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16237             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16238             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16239             today: "Today"
16240         }
16241     },
16242     
16243     modes: [
16244     {
16245         clsName: 'days',
16246         navFnc: 'Month',
16247         navStep: 1
16248     },
16249     {
16250         clsName: 'months',
16251         navFnc: 'FullYear',
16252         navStep: 1
16253     },
16254     {
16255         clsName: 'years',
16256         navFnc: 'FullYear',
16257         navStep: 10
16258     }]
16259 });
16260
16261 Roo.apply(Roo.bootstrap.DateField,  {
16262   
16263     template : {
16264         tag: 'div',
16265         cls: 'datepicker dropdown-menu roo-dynamic',
16266         cn: [
16267         {
16268             tag: 'div',
16269             cls: 'datepicker-days',
16270             cn: [
16271             {
16272                 tag: 'table',
16273                 cls: 'table-condensed',
16274                 cn:[
16275                 Roo.bootstrap.DateField.head,
16276                 {
16277                     tag: 'tbody'
16278                 },
16279                 Roo.bootstrap.DateField.footer
16280                 ]
16281             }
16282             ]
16283         },
16284         {
16285             tag: 'div',
16286             cls: 'datepicker-months',
16287             cn: [
16288             {
16289                 tag: 'table',
16290                 cls: 'table-condensed',
16291                 cn:[
16292                 Roo.bootstrap.DateField.head,
16293                 Roo.bootstrap.DateField.content,
16294                 Roo.bootstrap.DateField.footer
16295                 ]
16296             }
16297             ]
16298         },
16299         {
16300             tag: 'div',
16301             cls: 'datepicker-years',
16302             cn: [
16303             {
16304                 tag: 'table',
16305                 cls: 'table-condensed',
16306                 cn:[
16307                 Roo.bootstrap.DateField.head,
16308                 Roo.bootstrap.DateField.content,
16309                 Roo.bootstrap.DateField.footer
16310                 ]
16311             }
16312             ]
16313         }
16314         ]
16315     }
16316 });
16317
16318  
16319
16320  /*
16321  * - LGPL
16322  *
16323  * TimeField
16324  * 
16325  */
16326
16327 /**
16328  * @class Roo.bootstrap.TimeField
16329  * @extends Roo.bootstrap.Input
16330  * Bootstrap DateField class
16331  * 
16332  * 
16333  * @constructor
16334  * Create a new TimeField
16335  * @param {Object} config The config object
16336  */
16337
16338 Roo.bootstrap.TimeField = function(config){
16339     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16340     this.addEvents({
16341             /**
16342              * @event show
16343              * Fires when this field show.
16344              * @param {Roo.bootstrap.DateField} thisthis
16345              * @param {Mixed} date The date value
16346              */
16347             show : true,
16348             /**
16349              * @event show
16350              * Fires when this field hide.
16351              * @param {Roo.bootstrap.DateField} this
16352              * @param {Mixed} date The date value
16353              */
16354             hide : true,
16355             /**
16356              * @event select
16357              * Fires when select a date.
16358              * @param {Roo.bootstrap.DateField} this
16359              * @param {Mixed} date The date value
16360              */
16361             select : true
16362         });
16363 };
16364
16365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16366     
16367     /**
16368      * @cfg {String} format
16369      * The default time format string which can be overriden for localization support.  The format must be
16370      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16371      */
16372     format : "H:i",
16373        
16374     onRender: function(ct, position)
16375     {
16376         
16377         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16378                 
16379         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16380         
16381         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16382         
16383         this.pop = this.picker().select('>.datepicker-time',true).first();
16384         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16385         
16386         this.picker().on('mousedown', this.onMousedown, this);
16387         this.picker().on('click', this.onClick, this);
16388         
16389         this.picker().addClass('datepicker-dropdown');
16390     
16391         this.fillTime();
16392         this.update();
16393             
16394         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16395         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16396         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16397         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16398         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16399         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16400
16401     },
16402     
16403     fireKey: function(e){
16404         if (!this.picker().isVisible()){
16405             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16406                 this.show();
16407             }
16408             return;
16409         }
16410
16411         e.preventDefault();
16412         
16413         switch(e.keyCode){
16414             case 27: // escape
16415                 this.hide();
16416                 break;
16417             case 37: // left
16418             case 39: // right
16419                 this.onTogglePeriod();
16420                 break;
16421             case 38: // up
16422                 this.onIncrementMinutes();
16423                 break;
16424             case 40: // down
16425                 this.onDecrementMinutes();
16426                 break;
16427             case 13: // enter
16428             case 9: // tab
16429                 this.setTime();
16430                 break;
16431         }
16432     },
16433     
16434     onClick: function(e) {
16435         e.stopPropagation();
16436         e.preventDefault();
16437     },
16438     
16439     picker : function()
16440     {
16441         return this.el.select('.datepicker', true).first();
16442     },
16443     
16444     fillTime: function()
16445     {    
16446         var time = this.pop.select('tbody', true).first();
16447         
16448         time.dom.innerHTML = '';
16449         
16450         time.createChild({
16451             tag: 'tr',
16452             cn: [
16453                 {
16454                     tag: 'td',
16455                     cn: [
16456                         {
16457                             tag: 'a',
16458                             href: '#',
16459                             cls: 'btn',
16460                             cn: [
16461                                 {
16462                                     tag: 'span',
16463                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16464                                 }
16465                             ]
16466                         } 
16467                     ]
16468                 },
16469                 {
16470                     tag: 'td',
16471                     cls: 'separator'
16472                 },
16473                 {
16474                     tag: 'td',
16475                     cn: [
16476                         {
16477                             tag: 'a',
16478                             href: '#',
16479                             cls: 'btn',
16480                             cn: [
16481                                 {
16482                                     tag: 'span',
16483                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16484                                 }
16485                             ]
16486                         }
16487                     ]
16488                 },
16489                 {
16490                     tag: 'td',
16491                     cls: 'separator'
16492                 }
16493             ]
16494         });
16495         
16496         time.createChild({
16497             tag: 'tr',
16498             cn: [
16499                 {
16500                     tag: 'td',
16501                     cn: [
16502                         {
16503                             tag: 'span',
16504                             cls: 'timepicker-hour',
16505                             html: '00'
16506                         }  
16507                     ]
16508                 },
16509                 {
16510                     tag: 'td',
16511                     cls: 'separator',
16512                     html: ':'
16513                 },
16514                 {
16515                     tag: 'td',
16516                     cn: [
16517                         {
16518                             tag: 'span',
16519                             cls: 'timepicker-minute',
16520                             html: '00'
16521                         }  
16522                     ]
16523                 },
16524                 {
16525                     tag: 'td',
16526                     cls: 'separator'
16527                 },
16528                 {
16529                     tag: 'td',
16530                     cn: [
16531                         {
16532                             tag: 'button',
16533                             type: 'button',
16534                             cls: 'btn btn-primary period',
16535                             html: 'AM'
16536                             
16537                         }
16538                     ]
16539                 }
16540             ]
16541         });
16542         
16543         time.createChild({
16544             tag: 'tr',
16545             cn: [
16546                 {
16547                     tag: 'td',
16548                     cn: [
16549                         {
16550                             tag: 'a',
16551                             href: '#',
16552                             cls: 'btn',
16553                             cn: [
16554                                 {
16555                                     tag: 'span',
16556                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16557                                 }
16558                             ]
16559                         }
16560                     ]
16561                 },
16562                 {
16563                     tag: 'td',
16564                     cls: 'separator'
16565                 },
16566                 {
16567                     tag: 'td',
16568                     cn: [
16569                         {
16570                             tag: 'a',
16571                             href: '#',
16572                             cls: 'btn',
16573                             cn: [
16574                                 {
16575                                     tag: 'span',
16576                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16577                                 }
16578                             ]
16579                         }
16580                     ]
16581                 },
16582                 {
16583                     tag: 'td',
16584                     cls: 'separator'
16585                 }
16586             ]
16587         });
16588         
16589     },
16590     
16591     update: function()
16592     {
16593         
16594         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16595         
16596         this.fill();
16597     },
16598     
16599     fill: function() 
16600     {
16601         var hours = this.time.getHours();
16602         var minutes = this.time.getMinutes();
16603         var period = 'AM';
16604         
16605         if(hours > 11){
16606             period = 'PM';
16607         }
16608         
16609         if(hours == 0){
16610             hours = 12;
16611         }
16612         
16613         
16614         if(hours > 12){
16615             hours = hours - 12;
16616         }
16617         
16618         if(hours < 10){
16619             hours = '0' + hours;
16620         }
16621         
16622         if(minutes < 10){
16623             minutes = '0' + minutes;
16624         }
16625         
16626         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16627         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16628         this.pop.select('button', true).first().dom.innerHTML = period;
16629         
16630     },
16631     
16632     place: function()
16633     {   
16634         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16635         
16636         var cls = ['bottom'];
16637         
16638         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16639             cls.pop();
16640             cls.push('top');
16641         }
16642         
16643         cls.push('right');
16644         
16645         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16646             cls.pop();
16647             cls.push('left');
16648         }
16649         
16650         this.picker().addClass(cls.join('-'));
16651         
16652         var _this = this;
16653         
16654         Roo.each(cls, function(c){
16655             if(c == 'bottom'){
16656                 _this.picker().setTop(_this.inputEl().getHeight());
16657                 return;
16658             }
16659             if(c == 'top'){
16660                 _this.picker().setTop(0 - _this.picker().getHeight());
16661                 return;
16662             }
16663             
16664             if(c == 'left'){
16665                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16666                 return;
16667             }
16668             if(c == 'right'){
16669                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16670                 return;
16671             }
16672         });
16673         
16674     },
16675   
16676     onFocus : function()
16677     {
16678         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16679         this.show();
16680     },
16681     
16682     onBlur : function()
16683     {
16684         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16685         this.hide();
16686     },
16687     
16688     show : function()
16689     {
16690         this.picker().show();
16691         this.pop.show();
16692         this.update();
16693         this.place();
16694         
16695         this.fireEvent('show', this, this.date);
16696     },
16697     
16698     hide : function()
16699     {
16700         this.picker().hide();
16701         this.pop.hide();
16702         
16703         this.fireEvent('hide', this, this.date);
16704     },
16705     
16706     setTime : function()
16707     {
16708         this.hide();
16709         this.setValue(this.time.format(this.format));
16710         
16711         this.fireEvent('select', this, this.date);
16712         
16713         
16714     },
16715     
16716     onMousedown: function(e){
16717         e.stopPropagation();
16718         e.preventDefault();
16719     },
16720     
16721     onIncrementHours: function()
16722     {
16723         Roo.log('onIncrementHours');
16724         this.time = this.time.add(Date.HOUR, 1);
16725         this.update();
16726         
16727     },
16728     
16729     onDecrementHours: function()
16730     {
16731         Roo.log('onDecrementHours');
16732         this.time = this.time.add(Date.HOUR, -1);
16733         this.update();
16734     },
16735     
16736     onIncrementMinutes: function()
16737     {
16738         Roo.log('onIncrementMinutes');
16739         this.time = this.time.add(Date.MINUTE, 1);
16740         this.update();
16741     },
16742     
16743     onDecrementMinutes: function()
16744     {
16745         Roo.log('onDecrementMinutes');
16746         this.time = this.time.add(Date.MINUTE, -1);
16747         this.update();
16748     },
16749     
16750     onTogglePeriod: function()
16751     {
16752         Roo.log('onTogglePeriod');
16753         this.time = this.time.add(Date.HOUR, 12);
16754         this.update();
16755     }
16756     
16757    
16758 });
16759
16760 Roo.apply(Roo.bootstrap.TimeField,  {
16761     
16762     content : {
16763         tag: 'tbody',
16764         cn: [
16765             {
16766                 tag: 'tr',
16767                 cn: [
16768                 {
16769                     tag: 'td',
16770                     colspan: '7'
16771                 }
16772                 ]
16773             }
16774         ]
16775     },
16776     
16777     footer : {
16778         tag: 'tfoot',
16779         cn: [
16780             {
16781                 tag: 'tr',
16782                 cn: [
16783                 {
16784                     tag: 'th',
16785                     colspan: '7',
16786                     cls: '',
16787                     cn: [
16788                         {
16789                             tag: 'button',
16790                             cls: 'btn btn-info ok',
16791                             html: 'OK'
16792                         }
16793                     ]
16794                 }
16795
16796                 ]
16797             }
16798         ]
16799     }
16800 });
16801
16802 Roo.apply(Roo.bootstrap.TimeField,  {
16803   
16804     template : {
16805         tag: 'div',
16806         cls: 'datepicker dropdown-menu',
16807         cn: [
16808             {
16809                 tag: 'div',
16810                 cls: 'datepicker-time',
16811                 cn: [
16812                 {
16813                     tag: 'table',
16814                     cls: 'table-condensed',
16815                     cn:[
16816                     Roo.bootstrap.TimeField.content,
16817                     Roo.bootstrap.TimeField.footer
16818                     ]
16819                 }
16820                 ]
16821             }
16822         ]
16823     }
16824 });
16825
16826  
16827
16828  /*
16829  * - LGPL
16830  *
16831  * MonthField
16832  * 
16833  */
16834
16835 /**
16836  * @class Roo.bootstrap.MonthField
16837  * @extends Roo.bootstrap.Input
16838  * Bootstrap MonthField class
16839  * 
16840  * @cfg {String} language default en
16841  * 
16842  * @constructor
16843  * Create a new MonthField
16844  * @param {Object} config The config object
16845  */
16846
16847 Roo.bootstrap.MonthField = function(config){
16848     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16849     
16850     this.addEvents({
16851         /**
16852          * @event show
16853          * Fires when this field show.
16854          * @param {Roo.bootstrap.MonthField} this
16855          * @param {Mixed} date The date value
16856          */
16857         show : true,
16858         /**
16859          * @event show
16860          * Fires when this field hide.
16861          * @param {Roo.bootstrap.MonthField} this
16862          * @param {Mixed} date The date value
16863          */
16864         hide : true,
16865         /**
16866          * @event select
16867          * Fires when select a date.
16868          * @param {Roo.bootstrap.MonthField} this
16869          * @param {String} oldvalue The old value
16870          * @param {String} newvalue The new value
16871          */
16872         select : true
16873     });
16874 };
16875
16876 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16877     
16878     onRender: function(ct, position)
16879     {
16880         
16881         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16882         
16883         this.language = this.language || 'en';
16884         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16885         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16886         
16887         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16888         this.isInline = false;
16889         this.isInput = true;
16890         this.component = this.el.select('.add-on', true).first() || false;
16891         this.component = (this.component && this.component.length === 0) ? false : this.component;
16892         this.hasInput = this.component && this.inputEL().length;
16893         
16894         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16895         
16896         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16897         
16898         this.picker().on('mousedown', this.onMousedown, this);
16899         this.picker().on('click', this.onClick, this);
16900         
16901         this.picker().addClass('datepicker-dropdown');
16902         
16903         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16904             v.setStyle('width', '189px');
16905         });
16906         
16907         this.fillMonths();
16908         
16909         this.update();
16910         
16911         if(this.isInline) {
16912             this.show();
16913         }
16914         
16915     },
16916     
16917     setValue: function(v, suppressEvent)
16918     {   
16919         var o = this.getValue();
16920         
16921         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16922         
16923         this.update();
16924
16925         if(suppressEvent !== true){
16926             this.fireEvent('select', this, o, v);
16927         }
16928         
16929     },
16930     
16931     getValue: function()
16932     {
16933         return this.value;
16934     },
16935     
16936     onClick: function(e) 
16937     {
16938         e.stopPropagation();
16939         e.preventDefault();
16940         
16941         var target = e.getTarget();
16942         
16943         if(target.nodeName.toLowerCase() === 'i'){
16944             target = Roo.get(target).dom.parentNode;
16945         }
16946         
16947         var nodeName = target.nodeName;
16948         var className = target.className;
16949         var html = target.innerHTML;
16950         
16951         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16952             return;
16953         }
16954         
16955         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16956         
16957         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16958         
16959         this.hide();
16960                         
16961     },
16962     
16963     picker : function()
16964     {
16965         return this.pickerEl;
16966     },
16967     
16968     fillMonths: function()
16969     {    
16970         var i = 0;
16971         var months = this.picker().select('>.datepicker-months td', true).first();
16972         
16973         months.dom.innerHTML = '';
16974         
16975         while (i < 12) {
16976             var month = {
16977                 tag: 'span',
16978                 cls: 'month',
16979                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16980             }
16981             
16982             months.createChild(month);
16983         }
16984         
16985     },
16986     
16987     update: function()
16988     {
16989         var _this = this;
16990         
16991         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16992             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16993         }
16994         
16995         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16996             e.removeClass('active');
16997             
16998             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16999                 e.addClass('active');
17000             }
17001         })
17002     },
17003     
17004     place: function()
17005     {
17006         if(this.isInline) return;
17007         
17008         this.picker().removeClass(['bottom', 'top']);
17009         
17010         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17011             /*
17012              * place to the top of element!
17013              *
17014              */
17015             
17016             this.picker().addClass('top');
17017             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17018             
17019             return;
17020         }
17021         
17022         this.picker().addClass('bottom');
17023         
17024         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17025     },
17026     
17027     onFocus : function()
17028     {
17029         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17030         this.show();
17031     },
17032     
17033     onBlur : function()
17034     {
17035         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17036         
17037         var d = this.inputEl().getValue();
17038         
17039         this.setValue(d);
17040                 
17041         this.hide();
17042     },
17043     
17044     show : function()
17045     {
17046         this.picker().show();
17047         this.picker().select('>.datepicker-months', true).first().show();
17048         this.update();
17049         this.place();
17050         
17051         this.fireEvent('show', this, this.date);
17052     },
17053     
17054     hide : function()
17055     {
17056         if(this.isInline) return;
17057         this.picker().hide();
17058         this.fireEvent('hide', this, this.date);
17059         
17060     },
17061     
17062     onMousedown: function(e)
17063     {
17064         e.stopPropagation();
17065         e.preventDefault();
17066     },
17067     
17068     keyup: function(e)
17069     {
17070         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17071         this.update();
17072     },
17073
17074     fireKey: function(e)
17075     {
17076         if (!this.picker().isVisible()){
17077             if (e.keyCode == 27) // allow escape to hide and re-show picker
17078                 this.show();
17079             return;
17080         }
17081         
17082         var dir;
17083         
17084         switch(e.keyCode){
17085             case 27: // escape
17086                 this.hide();
17087                 e.preventDefault();
17088                 break;
17089             case 37: // left
17090             case 39: // right
17091                 dir = e.keyCode == 37 ? -1 : 1;
17092                 
17093                 this.vIndex = this.vIndex + dir;
17094                 
17095                 if(this.vIndex < 0){
17096                     this.vIndex = 0;
17097                 }
17098                 
17099                 if(this.vIndex > 11){
17100                     this.vIndex = 11;
17101                 }
17102                 
17103                 if(isNaN(this.vIndex)){
17104                     this.vIndex = 0;
17105                 }
17106                 
17107                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17108                 
17109                 break;
17110             case 38: // up
17111             case 40: // down
17112                 
17113                 dir = e.keyCode == 38 ? -1 : 1;
17114                 
17115                 this.vIndex = this.vIndex + dir * 4;
17116                 
17117                 if(this.vIndex < 0){
17118                     this.vIndex = 0;
17119                 }
17120                 
17121                 if(this.vIndex > 11){
17122                     this.vIndex = 11;
17123                 }
17124                 
17125                 if(isNaN(this.vIndex)){
17126                     this.vIndex = 0;
17127                 }
17128                 
17129                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17130                 break;
17131                 
17132             case 13: // enter
17133                 
17134                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17135                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17136                 }
17137                 
17138                 this.hide();
17139                 e.preventDefault();
17140                 break;
17141             case 9: // tab
17142                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17143                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17144                 }
17145                 this.hide();
17146                 break;
17147             case 16: // shift
17148             case 17: // ctrl
17149             case 18: // alt
17150                 break;
17151             default :
17152                 this.hide();
17153                 
17154         }
17155     },
17156     
17157     remove: function() 
17158     {
17159         this.picker().remove();
17160     }
17161    
17162 });
17163
17164 Roo.apply(Roo.bootstrap.MonthField,  {
17165     
17166     content : {
17167         tag: 'tbody',
17168         cn: [
17169         {
17170             tag: 'tr',
17171             cn: [
17172             {
17173                 tag: 'td',
17174                 colspan: '7'
17175             }
17176             ]
17177         }
17178         ]
17179     },
17180     
17181     dates:{
17182         en: {
17183             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17184             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17185         }
17186     }
17187 });
17188
17189 Roo.apply(Roo.bootstrap.MonthField,  {
17190   
17191     template : {
17192         tag: 'div',
17193         cls: 'datepicker dropdown-menu roo-dynamic',
17194         cn: [
17195             {
17196                 tag: 'div',
17197                 cls: 'datepicker-months',
17198                 cn: [
17199                 {
17200                     tag: 'table',
17201                     cls: 'table-condensed',
17202                     cn:[
17203                         Roo.bootstrap.DateField.content
17204                     ]
17205                 }
17206                 ]
17207             }
17208         ]
17209     }
17210 });
17211
17212  
17213
17214  
17215  /*
17216  * - LGPL
17217  *
17218  * CheckBox
17219  * 
17220  */
17221
17222 /**
17223  * @class Roo.bootstrap.CheckBox
17224  * @extends Roo.bootstrap.Input
17225  * Bootstrap CheckBox class
17226  * 
17227  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17228  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17229  * @cfg {String} boxLabel The text that appears beside the checkbox
17230  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17231  * @cfg {Boolean} checked initnal the element
17232  * @cfg {Boolean} inline inline the element (default false)
17233  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17234  * 
17235  * @constructor
17236  * Create a new CheckBox
17237  * @param {Object} config The config object
17238  */
17239
17240 Roo.bootstrap.CheckBox = function(config){
17241     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17242    
17243     this.addEvents({
17244         /**
17245         * @event check
17246         * Fires when the element is checked or unchecked.
17247         * @param {Roo.bootstrap.CheckBox} this This input
17248         * @param {Boolean} checked The new checked value
17249         */
17250        check : true
17251     });
17252     
17253 };
17254
17255 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17256   
17257     inputType: 'checkbox',
17258     inputValue: 1,
17259     valueOff: 0,
17260     boxLabel: false,
17261     checked: false,
17262     weight : false,
17263     inline: false,
17264     
17265     getAutoCreate : function()
17266     {
17267         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17268         
17269         var id = Roo.id();
17270         
17271         var cfg = {};
17272         
17273         cfg.cls = 'form-group ' + this.inputType; //input-group
17274         
17275         if(this.inline){
17276             cfg.cls += ' ' + this.inputType + '-inline';
17277         }
17278         
17279         var input =  {
17280             tag: 'input',
17281             id : id,
17282             type : this.inputType,
17283             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17284             cls : 'roo-' + this.inputType, //'form-box',
17285             placeholder : this.placeholder || ''
17286             
17287         };
17288         
17289         if (this.weight) { // Validity check?
17290             cfg.cls += " " + this.inputType + "-" + this.weight;
17291         }
17292         
17293         if (this.disabled) {
17294             input.disabled=true;
17295         }
17296         
17297         if(this.checked){
17298             input.checked = this.checked;
17299         }
17300         
17301         if (this.name) {
17302             input.name = this.name;
17303         }
17304         
17305         if (this.size) {
17306             input.cls += ' input-' + this.size;
17307         }
17308         
17309         var settings=this;
17310         
17311         ['xs','sm','md','lg'].map(function(size){
17312             if (settings[size]) {
17313                 cfg.cls += ' col-' + size + '-' + settings[size];
17314             }
17315         });
17316         
17317         var inputblock = input;
17318          
17319         if (this.before || this.after) {
17320             
17321             inputblock = {
17322                 cls : 'input-group',
17323                 cn :  [] 
17324             };
17325             
17326             if (this.before) {
17327                 inputblock.cn.push({
17328                     tag :'span',
17329                     cls : 'input-group-addon',
17330                     html : this.before
17331                 });
17332             }
17333             
17334             inputblock.cn.push(input);
17335             
17336             if (this.after) {
17337                 inputblock.cn.push({
17338                     tag :'span',
17339                     cls : 'input-group-addon',
17340                     html : this.after
17341                 });
17342             }
17343             
17344         }
17345         
17346         if (align ==='left' && this.fieldLabel.length) {
17347                 Roo.log("left and has label");
17348                 cfg.cn = [
17349                     
17350                     {
17351                         tag: 'label',
17352                         'for' :  id,
17353                         cls : 'control-label col-md-' + this.labelWidth,
17354                         html : this.fieldLabel
17355                         
17356                     },
17357                     {
17358                         cls : "col-md-" + (12 - this.labelWidth), 
17359                         cn: [
17360                             inputblock
17361                         ]
17362                     }
17363                     
17364                 ];
17365         } else if ( this.fieldLabel.length) {
17366                 Roo.log(" label");
17367                 cfg.cn = [
17368                    
17369                     {
17370                         tag: this.boxLabel ? 'span' : 'label',
17371                         'for': id,
17372                         cls: 'control-label box-input-label',
17373                         //cls : 'input-group-addon',
17374                         html : this.fieldLabel
17375                         
17376                     },
17377                     
17378                     inputblock
17379                     
17380                 ];
17381
17382         } else {
17383             
17384                 Roo.log(" no label && no align");
17385                 cfg.cn = [  inputblock ] ;
17386                 
17387                 
17388         }
17389         if(this.boxLabel){
17390              var boxLabelCfg = {
17391                 tag: 'label',
17392                 //'for': id, // box label is handled by onclick - so no for...
17393                 cls: 'box-label',
17394                 html: this.boxLabel
17395             }
17396             
17397             if(this.tooltip){
17398                 boxLabelCfg.tooltip = this.tooltip;
17399             }
17400              
17401             cfg.cn.push(boxLabelCfg);
17402         }
17403         
17404         
17405        
17406         return cfg;
17407         
17408     },
17409     
17410     /**
17411      * return the real input element.
17412      */
17413     inputEl: function ()
17414     {
17415         return this.el.select('input.roo-' + this.inputType,true).first();
17416     },
17417     
17418     labelEl: function()
17419     {
17420         return this.el.select('label.control-label',true).first();
17421     },
17422     /* depricated... */
17423     
17424     label: function()
17425     {
17426         return this.labelEl();
17427     },
17428     
17429     initEvents : function()
17430     {
17431 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17432         
17433         this.inputEl().on('click', this.onClick,  this);
17434         
17435         if (this.boxLabel) { 
17436             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17437         }
17438         
17439         this.startValue = this.getValue();
17440         
17441         if(this.groupId){
17442             Roo.bootstrap.CheckBox.register(this);
17443         }
17444     },
17445     
17446     onClick : function()
17447     {   
17448         this.setChecked(!this.checked);
17449     },
17450     
17451     setChecked : function(state,suppressEvent)
17452     {
17453         this.startValue = this.getValue();
17454         
17455         if(this.inputType == 'radio'){
17456             
17457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17458                 e.dom.checked = false;
17459             });
17460             
17461             this.inputEl().dom.checked = true;
17462             
17463             this.inputEl().dom.value = this.inputValue;
17464             
17465             if(suppressEvent !== true){
17466                 this.fireEvent('check', this, true);
17467             }
17468             
17469             this.validate();
17470             
17471             return;
17472         }
17473         
17474         this.checked = state;
17475         
17476         this.inputEl().dom.checked = state;
17477         
17478         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17479         
17480         if(suppressEvent !== true){
17481             this.fireEvent('check', this, state);
17482         }
17483         
17484         this.validate();
17485     },
17486     
17487     getValue : function()
17488     {
17489         if(this.inputType == 'radio'){
17490             return this.getGroupValue();
17491         }
17492         
17493         return this.inputEl().getValue();
17494         
17495     },
17496     
17497     getGroupValue : function()
17498     {
17499         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17500             return '';
17501         }
17502         
17503         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17504     },
17505     
17506     setValue : function(v,suppressEvent)
17507     {
17508         if(this.inputType == 'radio'){
17509             this.setGroupValue(v, suppressEvent);
17510             return;
17511         }
17512         
17513         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17514         
17515         this.validate();
17516     },
17517     
17518     setGroupValue : function(v, suppressEvent)
17519     {
17520         this.startValue = this.getValue();
17521         
17522         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17523             e.dom.checked = false;
17524             
17525             if(e.dom.value == v){
17526                 e.dom.checked = true;
17527             }
17528         });
17529         
17530         if(suppressEvent !== true){
17531             this.fireEvent('check', this, true);
17532         }
17533
17534         this.validate();
17535         
17536         return;
17537     },
17538     
17539     validate : function()
17540     {
17541         if(
17542                 this.disabled || 
17543                 (this.inputType == 'radio' && this.validateRadio()) ||
17544                 (this.inputType == 'checkbox' && this.validateCheckbox())
17545         ){
17546             this.markValid();
17547             return true;
17548         }
17549         
17550         this.markInvalid();
17551         return false;
17552     },
17553     
17554     validateRadio : function()
17555     {
17556         var valid = false;
17557         
17558         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17559             if(!e.dom.checked){
17560                 return;
17561             }
17562             
17563             valid = true;
17564             
17565             return false;
17566         });
17567         
17568         return valid;
17569     },
17570     
17571     validateCheckbox : function()
17572     {
17573         if(!this.groupId){
17574             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17575         }
17576         
17577         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17578         
17579         if(!group){
17580             return false;
17581         }
17582         
17583         var r = false;
17584         
17585         for(var i in group){
17586             if(r){
17587                 break;
17588             }
17589             
17590             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17591         }
17592         
17593         return r;
17594     },
17595     
17596     /**
17597      * Mark this field as valid
17598      */
17599     markValid : function()
17600     {
17601         if(this.allowBlank){
17602             return;
17603         }
17604         
17605         var _this = this;
17606         
17607         this.fireEvent('valid', this);
17608         
17609         if(this.inputType == 'radio'){
17610             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17611                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17612                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17613             });
17614             
17615             return;
17616         }
17617         
17618         if(!this.groupId){
17619             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17620             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17621             return;
17622         }
17623         
17624         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17625             
17626         if(!group){
17627             return;
17628         }
17629         
17630         for(var i in group){
17631             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17632             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17633         }
17634     },
17635     
17636      /**
17637      * Mark this field as invalid
17638      * @param {String} msg The validation message
17639      */
17640     markInvalid : function(msg)
17641     {
17642         if(this.allowBlank){
17643             return;
17644         }
17645         
17646         var _this = this;
17647         
17648         this.fireEvent('invalid', this, msg);
17649         
17650         if(this.inputType == 'radio'){
17651             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17652                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17653                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17654             });
17655             
17656             return;
17657         }
17658         
17659         if(!this.groupId){
17660             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17661             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17662             return;
17663         }
17664         
17665         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17666             
17667         if(!group){
17668             return;
17669         }
17670         
17671         for(var i in group){
17672             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17673             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17674         }
17675         
17676     }
17677     
17678 });
17679
17680 Roo.apply(Roo.bootstrap.CheckBox, {
17681     
17682     groups: {},
17683     
17684      /**
17685     * register a CheckBox Group
17686     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17687     */
17688     register : function(checkbox)
17689     {
17690         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17691             this.groups[checkbox.groupId] = {};
17692         }
17693         
17694         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17695             return;
17696         }
17697         
17698         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17699         
17700     },
17701     /**
17702     * fetch a CheckBox Group based on the group ID
17703     * @param {string} the group ID
17704     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17705     */
17706     get: function(groupId) {
17707         if (typeof(this.groups[groupId]) == 'undefined') {
17708             return false;
17709         }
17710         
17711         return this.groups[groupId] ;
17712     }
17713     
17714     
17715 });
17716 /*
17717  * - LGPL
17718  *
17719  * Radio
17720  *
17721  *
17722  * not inline
17723  *<div class="radio">
17724   <label>
17725     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17726     Option one is this and that&mdash;be sure to include why it's great
17727   </label>
17728 </div>
17729  *
17730  *
17731  *inline
17732  *<span>
17733  *<label class="radio-inline">fieldLabel</label>
17734  *<label class="radio-inline">
17735   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17736 </label>
17737 <span>
17738  * 
17739  * 
17740  */
17741
17742 /**
17743  * @class Roo.bootstrap.Radio
17744  * @extends Roo.bootstrap.CheckBox
17745  * Bootstrap Radio class
17746
17747  * @constructor
17748  * Create a new Radio
17749  * @param {Object} config The config object
17750  */
17751
17752 Roo.bootstrap.Radio = function(config){
17753     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17754    
17755 };
17756
17757 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17758     
17759     inputType: 'radio',
17760     inputValue: '',
17761     valueOff: '',
17762     
17763     getAutoCreate : function()
17764     {
17765         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17766         align = align || 'left'; // default...
17767         
17768         
17769         
17770         var id = Roo.id();
17771         
17772         var cfg = {
17773                 tag : this.inline ? 'span' : 'div',
17774                 cls : '',
17775                 cn : []
17776         };
17777         
17778         var inline = this.inline ? ' radio-inline' : '';
17779         
17780         var lbl = {
17781                 tag: 'label' ,
17782                 // does not need for, as we wrap the input with it..
17783                 'for' : id,
17784                 cls : 'control-label box-label' + inline,
17785                 cn : []
17786         };
17787         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17788         
17789         var fieldLabel = {
17790             tag: 'label' ,
17791             //cls : 'control-label' + inline,
17792             html : this.fieldLabel,
17793             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17794         };
17795         
17796  
17797         
17798         
17799         var input =  {
17800             tag: 'input',
17801             id : id,
17802             type : this.inputType,
17803             //value : (!this.checked) ? this.valueOff : this.inputValue,
17804             value : this.inputValue,
17805             cls : 'roo-radio',
17806             placeholder : this.placeholder || '' // ?? needed????
17807             
17808         };
17809         if (this.weight) { // Validity check?
17810             input.cls += " radio-" + this.weight;
17811         }
17812         if (this.disabled) {
17813             input.disabled=true;
17814         }
17815         
17816         if(this.checked){
17817             input.checked = this.checked;
17818         }
17819         
17820         if (this.name) {
17821             input.name = this.name;
17822         }
17823         
17824         if (this.size) {
17825             input.cls += ' input-' + this.size;
17826         }
17827         
17828         //?? can span's inline have a width??
17829         
17830         var settings=this;
17831         ['xs','sm','md','lg'].map(function(size){
17832             if (settings[size]) {
17833                 cfg.cls += ' col-' + size + '-' + settings[size];
17834             }
17835         });
17836         
17837         var inputblock = input;
17838         
17839         if (this.before || this.after) {
17840             
17841             inputblock = {
17842                 cls : 'input-group',
17843                 tag : 'span',
17844                 cn :  [] 
17845             };
17846             if (this.before) {
17847                 inputblock.cn.push({
17848                     tag :'span',
17849                     cls : 'input-group-addon',
17850                     html : this.before
17851                 });
17852             }
17853             inputblock.cn.push(input);
17854             if (this.after) {
17855                 inputblock.cn.push({
17856                     tag :'span',
17857                     cls : 'input-group-addon',
17858                     html : this.after
17859                 });
17860             }
17861             
17862         };
17863         
17864         
17865         if (this.fieldLabel && this.fieldLabel.length) {
17866             cfg.cn.push(fieldLabel);
17867         }
17868        
17869         // normal bootstrap puts the input inside the label.
17870         // however with our styled version - it has to go after the input.
17871        
17872         //lbl.cn.push(inputblock);
17873         
17874         var lblwrap =  {
17875             tag: 'span',
17876             cls: 'radio' + inline,
17877             cn: [
17878                 inputblock,
17879                 lbl
17880             ]
17881         };
17882         
17883         cfg.cn.push( lblwrap);
17884         
17885         if(this.boxLabel){
17886             lbl.cn.push({
17887                 tag: 'span',
17888                 html: this.boxLabel
17889             })
17890         }
17891          
17892         
17893         return cfg;
17894         
17895     },
17896     
17897     initEvents : function()
17898     {
17899 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17900         
17901         this.inputEl().on('click', this.onClick,  this);
17902         if (this.boxLabel) {
17903             Roo.log('find label')
17904             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17905         }
17906         
17907     },
17908     
17909     inputEl: function ()
17910     {
17911         return this.el.select('input.roo-radio',true).first();
17912     },
17913     onClick : function()
17914     {   
17915         Roo.log("click");
17916         this.setChecked(true);
17917     },
17918     
17919     setChecked : function(state,suppressEvent)
17920     {
17921         if(state){
17922             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17923                 v.dom.checked = false;
17924             });
17925         }
17926         Roo.log(this.inputEl().dom);
17927         this.checked = state;
17928         this.inputEl().dom.checked = state;
17929         
17930         if(suppressEvent !== true){
17931             this.fireEvent('check', this, state);
17932         }
17933         
17934         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17935         
17936     },
17937     
17938     getGroupValue : function()
17939     {
17940         var value = '';
17941         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17942             if(v.dom.checked == true){
17943                 value = v.dom.value;
17944             }
17945         });
17946         
17947         return value;
17948     },
17949     
17950     /**
17951      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17952      * @return {Mixed} value The field value
17953      */
17954     getValue : function(){
17955         return this.getGroupValue();
17956     }
17957     
17958 });
17959
17960  
17961 //<script type="text/javascript">
17962
17963 /*
17964  * Based  Ext JS Library 1.1.1
17965  * Copyright(c) 2006-2007, Ext JS, LLC.
17966  * LGPL
17967  *
17968  */
17969  
17970 /**
17971  * @class Roo.HtmlEditorCore
17972  * @extends Roo.Component
17973  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17974  *
17975  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17976  */
17977
17978 Roo.HtmlEditorCore = function(config){
17979     
17980     
17981     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17982     
17983     
17984     this.addEvents({
17985         /**
17986          * @event initialize
17987          * Fires when the editor is fully initialized (including the iframe)
17988          * @param {Roo.HtmlEditorCore} this
17989          */
17990         initialize: true,
17991         /**
17992          * @event activate
17993          * Fires when the editor is first receives the focus. Any insertion must wait
17994          * until after this event.
17995          * @param {Roo.HtmlEditorCore} this
17996          */
17997         activate: true,
17998          /**
17999          * @event beforesync
18000          * Fires before the textarea is updated with content from the editor iframe. Return false
18001          * to cancel the sync.
18002          * @param {Roo.HtmlEditorCore} this
18003          * @param {String} html
18004          */
18005         beforesync: true,
18006          /**
18007          * @event beforepush
18008          * Fires before the iframe editor is updated with content from the textarea. Return false
18009          * to cancel the push.
18010          * @param {Roo.HtmlEditorCore} this
18011          * @param {String} html
18012          */
18013         beforepush: true,
18014          /**
18015          * @event sync
18016          * Fires when the textarea is updated with content from the editor iframe.
18017          * @param {Roo.HtmlEditorCore} this
18018          * @param {String} html
18019          */
18020         sync: true,
18021          /**
18022          * @event push
18023          * Fires when the iframe editor is updated with content from the textarea.
18024          * @param {Roo.HtmlEditorCore} this
18025          * @param {String} html
18026          */
18027         push: true,
18028         
18029         /**
18030          * @event editorevent
18031          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18032          * @param {Roo.HtmlEditorCore} this
18033          */
18034         editorevent: true
18035         
18036     });
18037     
18038     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18039     
18040     // defaults : white / black...
18041     this.applyBlacklists();
18042     
18043     
18044     
18045 };
18046
18047
18048 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18049
18050
18051      /**
18052      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18053      */
18054     
18055     owner : false,
18056     
18057      /**
18058      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18059      *                        Roo.resizable.
18060      */
18061     resizable : false,
18062      /**
18063      * @cfg {Number} height (in pixels)
18064      */   
18065     height: 300,
18066    /**
18067      * @cfg {Number} width (in pixels)
18068      */   
18069     width: 500,
18070     
18071     /**
18072      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18073      * 
18074      */
18075     stylesheets: false,
18076     
18077     // id of frame..
18078     frameId: false,
18079     
18080     // private properties
18081     validationEvent : false,
18082     deferHeight: true,
18083     initialized : false,
18084     activated : false,
18085     sourceEditMode : false,
18086     onFocus : Roo.emptyFn,
18087     iframePad:3,
18088     hideMode:'offsets',
18089     
18090     clearUp: true,
18091     
18092     // blacklist + whitelisted elements..
18093     black: false,
18094     white: false,
18095      
18096     
18097
18098     /**
18099      * Protected method that will not generally be called directly. It
18100      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18101      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18102      */
18103     getDocMarkup : function(){
18104         // body styles..
18105         var st = '';
18106         
18107         // inherit styels from page...?? 
18108         if (this.stylesheets === false) {
18109             
18110             Roo.get(document.head).select('style').each(function(node) {
18111                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18112             });
18113             
18114             Roo.get(document.head).select('link').each(function(node) { 
18115                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18116             });
18117             
18118         } else if (!this.stylesheets.length) {
18119                 // simple..
18120                 st = '<style type="text/css">' +
18121                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18122                    '</style>';
18123         } else { 
18124             
18125         }
18126         
18127         st +=  '<style type="text/css">' +
18128             'IMG { cursor: pointer } ' +
18129         '</style>';
18130
18131         
18132         return '<html><head>' + st  +
18133             //<style type="text/css">' +
18134             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18135             //'</style>' +
18136             ' </head><body class="roo-htmleditor-body"></body></html>';
18137     },
18138
18139     // private
18140     onRender : function(ct, position)
18141     {
18142         var _t = this;
18143         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18144         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18145         
18146         
18147         this.el.dom.style.border = '0 none';
18148         this.el.dom.setAttribute('tabIndex', -1);
18149         this.el.addClass('x-hidden hide');
18150         
18151         
18152         
18153         if(Roo.isIE){ // fix IE 1px bogus margin
18154             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18155         }
18156        
18157         
18158         this.frameId = Roo.id();
18159         
18160          
18161         
18162         var iframe = this.owner.wrap.createChild({
18163             tag: 'iframe',
18164             cls: 'form-control', // bootstrap..
18165             id: this.frameId,
18166             name: this.frameId,
18167             frameBorder : 'no',
18168             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18169         }, this.el
18170         );
18171         
18172         
18173         this.iframe = iframe.dom;
18174
18175          this.assignDocWin();
18176         
18177         this.doc.designMode = 'on';
18178        
18179         this.doc.open();
18180         this.doc.write(this.getDocMarkup());
18181         this.doc.close();
18182
18183         
18184         var task = { // must defer to wait for browser to be ready
18185             run : function(){
18186                 //console.log("run task?" + this.doc.readyState);
18187                 this.assignDocWin();
18188                 if(this.doc.body || this.doc.readyState == 'complete'){
18189                     try {
18190                         this.doc.designMode="on";
18191                     } catch (e) {
18192                         return;
18193                     }
18194                     Roo.TaskMgr.stop(task);
18195                     this.initEditor.defer(10, this);
18196                 }
18197             },
18198             interval : 10,
18199             duration: 10000,
18200             scope: this
18201         };
18202         Roo.TaskMgr.start(task);
18203
18204     },
18205
18206     // private
18207     onResize : function(w, h)
18208     {
18209          Roo.log('resize: ' +w + ',' + h );
18210         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18211         if(!this.iframe){
18212             return;
18213         }
18214         if(typeof w == 'number'){
18215             
18216             this.iframe.style.width = w + 'px';
18217         }
18218         if(typeof h == 'number'){
18219             
18220             this.iframe.style.height = h + 'px';
18221             if(this.doc){
18222                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18223             }
18224         }
18225         
18226     },
18227
18228     /**
18229      * Toggles the editor between standard and source edit mode.
18230      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18231      */
18232     toggleSourceEdit : function(sourceEditMode){
18233         
18234         this.sourceEditMode = sourceEditMode === true;
18235         
18236         if(this.sourceEditMode){
18237  
18238             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18239             
18240         }else{
18241             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18242             //this.iframe.className = '';
18243             this.deferFocus();
18244         }
18245         //this.setSize(this.owner.wrap.getSize());
18246         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18247     },
18248
18249     
18250   
18251
18252     /**
18253      * Protected method that will not generally be called directly. If you need/want
18254      * custom HTML cleanup, this is the method you should override.
18255      * @param {String} html The HTML to be cleaned
18256      * return {String} The cleaned HTML
18257      */
18258     cleanHtml : function(html){
18259         html = String(html);
18260         if(html.length > 5){
18261             if(Roo.isSafari){ // strip safari nonsense
18262                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18263             }
18264         }
18265         if(html == '&nbsp;'){
18266             html = '';
18267         }
18268         return html;
18269     },
18270
18271     /**
18272      * HTML Editor -> Textarea
18273      * Protected method that will not generally be called directly. Syncs the contents
18274      * of the editor iframe with the textarea.
18275      */
18276     syncValue : function(){
18277         if(this.initialized){
18278             var bd = (this.doc.body || this.doc.documentElement);
18279             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18280             var html = bd.innerHTML;
18281             if(Roo.isSafari){
18282                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18283                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18284                 if(m && m[1]){
18285                     html = '<div style="'+m[0]+'">' + html + '</div>';
18286                 }
18287             }
18288             html = this.cleanHtml(html);
18289             // fix up the special chars.. normaly like back quotes in word...
18290             // however we do not want to do this with chinese..
18291             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18292                 var cc = b.charCodeAt();
18293                 if (
18294                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18295                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18296                     (cc >= 0xf900 && cc < 0xfb00 )
18297                 ) {
18298                         return b;
18299                 }
18300                 return "&#"+cc+";" 
18301             });
18302             if(this.owner.fireEvent('beforesync', this, html) !== false){
18303                 this.el.dom.value = html;
18304                 this.owner.fireEvent('sync', this, html);
18305             }
18306         }
18307     },
18308
18309     /**
18310      * Protected method that will not generally be called directly. Pushes the value of the textarea
18311      * into the iframe editor.
18312      */
18313     pushValue : function(){
18314         if(this.initialized){
18315             var v = this.el.dom.value.trim();
18316             
18317 //            if(v.length < 1){
18318 //                v = '&#160;';
18319 //            }
18320             
18321             if(this.owner.fireEvent('beforepush', this, v) !== false){
18322                 var d = (this.doc.body || this.doc.documentElement);
18323                 d.innerHTML = v;
18324                 this.cleanUpPaste();
18325                 this.el.dom.value = d.innerHTML;
18326                 this.owner.fireEvent('push', this, v);
18327             }
18328         }
18329     },
18330
18331     // private
18332     deferFocus : function(){
18333         this.focus.defer(10, this);
18334     },
18335
18336     // doc'ed in Field
18337     focus : function(){
18338         if(this.win && !this.sourceEditMode){
18339             this.win.focus();
18340         }else{
18341             this.el.focus();
18342         }
18343     },
18344     
18345     assignDocWin: function()
18346     {
18347         var iframe = this.iframe;
18348         
18349          if(Roo.isIE){
18350             this.doc = iframe.contentWindow.document;
18351             this.win = iframe.contentWindow;
18352         } else {
18353 //            if (!Roo.get(this.frameId)) {
18354 //                return;
18355 //            }
18356 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18357 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18358             
18359             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18360                 return;
18361             }
18362             
18363             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18364             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18365         }
18366     },
18367     
18368     // private
18369     initEditor : function(){
18370         //console.log("INIT EDITOR");
18371         this.assignDocWin();
18372         
18373         
18374         
18375         this.doc.designMode="on";
18376         this.doc.open();
18377         this.doc.write(this.getDocMarkup());
18378         this.doc.close();
18379         
18380         var dbody = (this.doc.body || this.doc.documentElement);
18381         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18382         // this copies styles from the containing element into thsi one..
18383         // not sure why we need all of this..
18384         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18385         
18386         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18387         //ss['background-attachment'] = 'fixed'; // w3c
18388         dbody.bgProperties = 'fixed'; // ie
18389         //Roo.DomHelper.applyStyles(dbody, ss);
18390         Roo.EventManager.on(this.doc, {
18391             //'mousedown': this.onEditorEvent,
18392             'mouseup': this.onEditorEvent,
18393             'dblclick': this.onEditorEvent,
18394             'click': this.onEditorEvent,
18395             'keyup': this.onEditorEvent,
18396             buffer:100,
18397             scope: this
18398         });
18399         if(Roo.isGecko){
18400             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18401         }
18402         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18403             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18404         }
18405         this.initialized = true;
18406
18407         this.owner.fireEvent('initialize', this);
18408         this.pushValue();
18409     },
18410
18411     // private
18412     onDestroy : function(){
18413         
18414         
18415         
18416         if(this.rendered){
18417             
18418             //for (var i =0; i < this.toolbars.length;i++) {
18419             //    // fixme - ask toolbars for heights?
18420             //    this.toolbars[i].onDestroy();
18421            // }
18422             
18423             //this.wrap.dom.innerHTML = '';
18424             //this.wrap.remove();
18425         }
18426     },
18427
18428     // private
18429     onFirstFocus : function(){
18430         
18431         this.assignDocWin();
18432         
18433         
18434         this.activated = true;
18435          
18436     
18437         if(Roo.isGecko){ // prevent silly gecko errors
18438             this.win.focus();
18439             var s = this.win.getSelection();
18440             if(!s.focusNode || s.focusNode.nodeType != 3){
18441                 var r = s.getRangeAt(0);
18442                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18443                 r.collapse(true);
18444                 this.deferFocus();
18445             }
18446             try{
18447                 this.execCmd('useCSS', true);
18448                 this.execCmd('styleWithCSS', false);
18449             }catch(e){}
18450         }
18451         this.owner.fireEvent('activate', this);
18452     },
18453
18454     // private
18455     adjustFont: function(btn){
18456         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18457         //if(Roo.isSafari){ // safari
18458         //    adjust *= 2;
18459        // }
18460         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18461         if(Roo.isSafari){ // safari
18462             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18463             v =  (v < 10) ? 10 : v;
18464             v =  (v > 48) ? 48 : v;
18465             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18466             
18467         }
18468         
18469         
18470         v = Math.max(1, v+adjust);
18471         
18472         this.execCmd('FontSize', v  );
18473     },
18474
18475     onEditorEvent : function(e)
18476     {
18477         this.owner.fireEvent('editorevent', this, e);
18478       //  this.updateToolbar();
18479         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18480     },
18481
18482     insertTag : function(tg)
18483     {
18484         // could be a bit smarter... -> wrap the current selected tRoo..
18485         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18486             
18487             range = this.createRange(this.getSelection());
18488             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18489             wrappingNode.appendChild(range.extractContents());
18490             range.insertNode(wrappingNode);
18491
18492             return;
18493             
18494             
18495             
18496         }
18497         this.execCmd("formatblock",   tg);
18498         
18499     },
18500     
18501     insertText : function(txt)
18502     {
18503         
18504         
18505         var range = this.createRange();
18506         range.deleteContents();
18507                //alert(Sender.getAttribute('label'));
18508                
18509         range.insertNode(this.doc.createTextNode(txt));
18510     } ,
18511     
18512      
18513
18514     /**
18515      * Executes a Midas editor command on the editor document and performs necessary focus and
18516      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18517      * @param {String} cmd The Midas command
18518      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18519      */
18520     relayCmd : function(cmd, value){
18521         this.win.focus();
18522         this.execCmd(cmd, value);
18523         this.owner.fireEvent('editorevent', this);
18524         //this.updateToolbar();
18525         this.owner.deferFocus();
18526     },
18527
18528     /**
18529      * Executes a Midas editor command directly on the editor document.
18530      * For visual commands, you should use {@link #relayCmd} instead.
18531      * <b>This should only be called after the editor is initialized.</b>
18532      * @param {String} cmd The Midas command
18533      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18534      */
18535     execCmd : function(cmd, value){
18536         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18537         this.syncValue();
18538     },
18539  
18540  
18541    
18542     /**
18543      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18544      * to insert tRoo.
18545      * @param {String} text | dom node.. 
18546      */
18547     insertAtCursor : function(text)
18548     {
18549         
18550         
18551         
18552         if(!this.activated){
18553             return;
18554         }
18555         /*
18556         if(Roo.isIE){
18557             this.win.focus();
18558             var r = this.doc.selection.createRange();
18559             if(r){
18560                 r.collapse(true);
18561                 r.pasteHTML(text);
18562                 this.syncValue();
18563                 this.deferFocus();
18564             
18565             }
18566             return;
18567         }
18568         */
18569         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18570             this.win.focus();
18571             
18572             
18573             // from jquery ui (MIT licenced)
18574             var range, node;
18575             var win = this.win;
18576             
18577             if (win.getSelection && win.getSelection().getRangeAt) {
18578                 range = win.getSelection().getRangeAt(0);
18579                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18580                 range.insertNode(node);
18581             } else if (win.document.selection && win.document.selection.createRange) {
18582                 // no firefox support
18583                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18584                 win.document.selection.createRange().pasteHTML(txt);
18585             } else {
18586                 // no firefox support
18587                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18588                 this.execCmd('InsertHTML', txt);
18589             } 
18590             
18591             this.syncValue();
18592             
18593             this.deferFocus();
18594         }
18595     },
18596  // private
18597     mozKeyPress : function(e){
18598         if(e.ctrlKey){
18599             var c = e.getCharCode(), cmd;
18600           
18601             if(c > 0){
18602                 c = String.fromCharCode(c).toLowerCase();
18603                 switch(c){
18604                     case 'b':
18605                         cmd = 'bold';
18606                         break;
18607                     case 'i':
18608                         cmd = 'italic';
18609                         break;
18610                     
18611                     case 'u':
18612                         cmd = 'underline';
18613                         break;
18614                     
18615                     case 'v':
18616                         this.cleanUpPaste.defer(100, this);
18617                         return;
18618                         
18619                 }
18620                 if(cmd){
18621                     this.win.focus();
18622                     this.execCmd(cmd);
18623                     this.deferFocus();
18624                     e.preventDefault();
18625                 }
18626                 
18627             }
18628         }
18629     },
18630
18631     // private
18632     fixKeys : function(){ // load time branching for fastest keydown performance
18633         if(Roo.isIE){
18634             return function(e){
18635                 var k = e.getKey(), r;
18636                 if(k == e.TAB){
18637                     e.stopEvent();
18638                     r = this.doc.selection.createRange();
18639                     if(r){
18640                         r.collapse(true);
18641                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18642                         this.deferFocus();
18643                     }
18644                     return;
18645                 }
18646                 
18647                 if(k == e.ENTER){
18648                     r = this.doc.selection.createRange();
18649                     if(r){
18650                         var target = r.parentElement();
18651                         if(!target || target.tagName.toLowerCase() != 'li'){
18652                             e.stopEvent();
18653                             r.pasteHTML('<br />');
18654                             r.collapse(false);
18655                             r.select();
18656                         }
18657                     }
18658                 }
18659                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18660                     this.cleanUpPaste.defer(100, this);
18661                     return;
18662                 }
18663                 
18664                 
18665             };
18666         }else if(Roo.isOpera){
18667             return function(e){
18668                 var k = e.getKey();
18669                 if(k == e.TAB){
18670                     e.stopEvent();
18671                     this.win.focus();
18672                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18673                     this.deferFocus();
18674                 }
18675                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18676                     this.cleanUpPaste.defer(100, this);
18677                     return;
18678                 }
18679                 
18680             };
18681         }else if(Roo.isSafari){
18682             return function(e){
18683                 var k = e.getKey();
18684                 
18685                 if(k == e.TAB){
18686                     e.stopEvent();
18687                     this.execCmd('InsertText','\t');
18688                     this.deferFocus();
18689                     return;
18690                 }
18691                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18692                     this.cleanUpPaste.defer(100, this);
18693                     return;
18694                 }
18695                 
18696              };
18697         }
18698     }(),
18699     
18700     getAllAncestors: function()
18701     {
18702         var p = this.getSelectedNode();
18703         var a = [];
18704         if (!p) {
18705             a.push(p); // push blank onto stack..
18706             p = this.getParentElement();
18707         }
18708         
18709         
18710         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18711             a.push(p);
18712             p = p.parentNode;
18713         }
18714         a.push(this.doc.body);
18715         return a;
18716     },
18717     lastSel : false,
18718     lastSelNode : false,
18719     
18720     
18721     getSelection : function() 
18722     {
18723         this.assignDocWin();
18724         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18725     },
18726     
18727     getSelectedNode: function() 
18728     {
18729         // this may only work on Gecko!!!
18730         
18731         // should we cache this!!!!
18732         
18733         
18734         
18735          
18736         var range = this.createRange(this.getSelection()).cloneRange();
18737         
18738         if (Roo.isIE) {
18739             var parent = range.parentElement();
18740             while (true) {
18741                 var testRange = range.duplicate();
18742                 testRange.moveToElementText(parent);
18743                 if (testRange.inRange(range)) {
18744                     break;
18745                 }
18746                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18747                     break;
18748                 }
18749                 parent = parent.parentElement;
18750             }
18751             return parent;
18752         }
18753         
18754         // is ancestor a text element.
18755         var ac =  range.commonAncestorContainer;
18756         if (ac.nodeType == 3) {
18757             ac = ac.parentNode;
18758         }
18759         
18760         var ar = ac.childNodes;
18761          
18762         var nodes = [];
18763         var other_nodes = [];
18764         var has_other_nodes = false;
18765         for (var i=0;i<ar.length;i++) {
18766             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18767                 continue;
18768             }
18769             // fullly contained node.
18770             
18771             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18772                 nodes.push(ar[i]);
18773                 continue;
18774             }
18775             
18776             // probably selected..
18777             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18778                 other_nodes.push(ar[i]);
18779                 continue;
18780             }
18781             // outer..
18782             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18783                 continue;
18784             }
18785             
18786             
18787             has_other_nodes = true;
18788         }
18789         if (!nodes.length && other_nodes.length) {
18790             nodes= other_nodes;
18791         }
18792         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18793             return false;
18794         }
18795         
18796         return nodes[0];
18797     },
18798     createRange: function(sel)
18799     {
18800         // this has strange effects when using with 
18801         // top toolbar - not sure if it's a great idea.
18802         //this.editor.contentWindow.focus();
18803         if (typeof sel != "undefined") {
18804             try {
18805                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18806             } catch(e) {
18807                 return this.doc.createRange();
18808             }
18809         } else {
18810             return this.doc.createRange();
18811         }
18812     },
18813     getParentElement: function()
18814     {
18815         
18816         this.assignDocWin();
18817         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18818         
18819         var range = this.createRange(sel);
18820          
18821         try {
18822             var p = range.commonAncestorContainer;
18823             while (p.nodeType == 3) { // text node
18824                 p = p.parentNode;
18825             }
18826             return p;
18827         } catch (e) {
18828             return null;
18829         }
18830     
18831     },
18832     /***
18833      *
18834      * Range intersection.. the hard stuff...
18835      *  '-1' = before
18836      *  '0' = hits..
18837      *  '1' = after.
18838      *         [ -- selected range --- ]
18839      *   [fail]                        [fail]
18840      *
18841      *    basically..
18842      *      if end is before start or  hits it. fail.
18843      *      if start is after end or hits it fail.
18844      *
18845      *   if either hits (but other is outside. - then it's not 
18846      *   
18847      *    
18848      **/
18849     
18850     
18851     // @see http://www.thismuchiknow.co.uk/?p=64.
18852     rangeIntersectsNode : function(range, node)
18853     {
18854         var nodeRange = node.ownerDocument.createRange();
18855         try {
18856             nodeRange.selectNode(node);
18857         } catch (e) {
18858             nodeRange.selectNodeContents(node);
18859         }
18860     
18861         var rangeStartRange = range.cloneRange();
18862         rangeStartRange.collapse(true);
18863     
18864         var rangeEndRange = range.cloneRange();
18865         rangeEndRange.collapse(false);
18866     
18867         var nodeStartRange = nodeRange.cloneRange();
18868         nodeStartRange.collapse(true);
18869     
18870         var nodeEndRange = nodeRange.cloneRange();
18871         nodeEndRange.collapse(false);
18872     
18873         return rangeStartRange.compareBoundaryPoints(
18874                  Range.START_TO_START, nodeEndRange) == -1 &&
18875                rangeEndRange.compareBoundaryPoints(
18876                  Range.START_TO_START, nodeStartRange) == 1;
18877         
18878          
18879     },
18880     rangeCompareNode : function(range, node)
18881     {
18882         var nodeRange = node.ownerDocument.createRange();
18883         try {
18884             nodeRange.selectNode(node);
18885         } catch (e) {
18886             nodeRange.selectNodeContents(node);
18887         }
18888         
18889         
18890         range.collapse(true);
18891     
18892         nodeRange.collapse(true);
18893      
18894         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18895         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18896          
18897         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18898         
18899         var nodeIsBefore   =  ss == 1;
18900         var nodeIsAfter    = ee == -1;
18901         
18902         if (nodeIsBefore && nodeIsAfter)
18903             return 0; // outer
18904         if (!nodeIsBefore && nodeIsAfter)
18905             return 1; //right trailed.
18906         
18907         if (nodeIsBefore && !nodeIsAfter)
18908             return 2;  // left trailed.
18909         // fully contined.
18910         return 3;
18911     },
18912
18913     // private? - in a new class?
18914     cleanUpPaste :  function()
18915     {
18916         // cleans up the whole document..
18917         Roo.log('cleanuppaste');
18918         
18919         this.cleanUpChildren(this.doc.body);
18920         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18921         if (clean != this.doc.body.innerHTML) {
18922             this.doc.body.innerHTML = clean;
18923         }
18924         
18925     },
18926     
18927     cleanWordChars : function(input) {// change the chars to hex code
18928         var he = Roo.HtmlEditorCore;
18929         
18930         var output = input;
18931         Roo.each(he.swapCodes, function(sw) { 
18932             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18933             
18934             output = output.replace(swapper, sw[1]);
18935         });
18936         
18937         return output;
18938     },
18939     
18940     
18941     cleanUpChildren : function (n)
18942     {
18943         if (!n.childNodes.length) {
18944             return;
18945         }
18946         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18947            this.cleanUpChild(n.childNodes[i]);
18948         }
18949     },
18950     
18951     
18952         
18953     
18954     cleanUpChild : function (node)
18955     {
18956         var ed = this;
18957         //console.log(node);
18958         if (node.nodeName == "#text") {
18959             // clean up silly Windows -- stuff?
18960             return; 
18961         }
18962         if (node.nodeName == "#comment") {
18963             node.parentNode.removeChild(node);
18964             // clean up silly Windows -- stuff?
18965             return; 
18966         }
18967         var lcname = node.tagName.toLowerCase();
18968         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18969         // whitelist of tags..
18970         
18971         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18972             // remove node.
18973             node.parentNode.removeChild(node);
18974             return;
18975             
18976         }
18977         
18978         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18979         
18980         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18981         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18982         
18983         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18984         //    remove_keep_children = true;
18985         //}
18986         
18987         if (remove_keep_children) {
18988             this.cleanUpChildren(node);
18989             // inserts everything just before this node...
18990             while (node.childNodes.length) {
18991                 var cn = node.childNodes[0];
18992                 node.removeChild(cn);
18993                 node.parentNode.insertBefore(cn, node);
18994             }
18995             node.parentNode.removeChild(node);
18996             return;
18997         }
18998         
18999         if (!node.attributes || !node.attributes.length) {
19000             this.cleanUpChildren(node);
19001             return;
19002         }
19003         
19004         function cleanAttr(n,v)
19005         {
19006             
19007             if (v.match(/^\./) || v.match(/^\//)) {
19008                 return;
19009             }
19010             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19011                 return;
19012             }
19013             if (v.match(/^#/)) {
19014                 return;
19015             }
19016 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19017             node.removeAttribute(n);
19018             
19019         }
19020         
19021         var cwhite = this.cwhite;
19022         var cblack = this.cblack;
19023             
19024         function cleanStyle(n,v)
19025         {
19026             if (v.match(/expression/)) { //XSS?? should we even bother..
19027                 node.removeAttribute(n);
19028                 return;
19029             }
19030             
19031             var parts = v.split(/;/);
19032             var clean = [];
19033             
19034             Roo.each(parts, function(p) {
19035                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19036                 if (!p.length) {
19037                     return true;
19038                 }
19039                 var l = p.split(':').shift().replace(/\s+/g,'');
19040                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19041                 
19042                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19043 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19044                     //node.removeAttribute(n);
19045                     return true;
19046                 }
19047                 //Roo.log()
19048                 // only allow 'c whitelisted system attributes'
19049                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19050 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19051                     //node.removeAttribute(n);
19052                     return true;
19053                 }
19054                 
19055                 
19056                  
19057                 
19058                 clean.push(p);
19059                 return true;
19060             });
19061             if (clean.length) { 
19062                 node.setAttribute(n, clean.join(';'));
19063             } else {
19064                 node.removeAttribute(n);
19065             }
19066             
19067         }
19068         
19069         
19070         for (var i = node.attributes.length-1; i > -1 ; i--) {
19071             var a = node.attributes[i];
19072             //console.log(a);
19073             
19074             if (a.name.toLowerCase().substr(0,2)=='on')  {
19075                 node.removeAttribute(a.name);
19076                 continue;
19077             }
19078             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19079                 node.removeAttribute(a.name);
19080                 continue;
19081             }
19082             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19083                 cleanAttr(a.name,a.value); // fixme..
19084                 continue;
19085             }
19086             if (a.name == 'style') {
19087                 cleanStyle(a.name,a.value);
19088                 continue;
19089             }
19090             /// clean up MS crap..
19091             // tecnically this should be a list of valid class'es..
19092             
19093             
19094             if (a.name == 'class') {
19095                 if (a.value.match(/^Mso/)) {
19096                     node.className = '';
19097                 }
19098                 
19099                 if (a.value.match(/body/)) {
19100                     node.className = '';
19101                 }
19102                 continue;
19103             }
19104             
19105             // style cleanup!?
19106             // class cleanup?
19107             
19108         }
19109         
19110         
19111         this.cleanUpChildren(node);
19112         
19113         
19114     },
19115     /**
19116      * Clean up MS wordisms...
19117      */
19118     cleanWord : function(node)
19119     {
19120         var _t = this;
19121         var cleanWordChildren = function()
19122         {
19123             if (!node.childNodes.length) {
19124                 return;
19125             }
19126             for (var i = node.childNodes.length-1; i > -1 ; i--) {
19127                _t.cleanWord(node.childNodes[i]);
19128             }
19129         }
19130         
19131         
19132         if (!node) {
19133             this.cleanWord(this.doc.body);
19134             return;
19135         }
19136         if (node.nodeName == "#text") {
19137             // clean up silly Windows -- stuff?
19138             return; 
19139         }
19140         if (node.nodeName == "#comment") {
19141             node.parentNode.removeChild(node);
19142             // clean up silly Windows -- stuff?
19143             return; 
19144         }
19145         
19146         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19147             node.parentNode.removeChild(node);
19148             return;
19149         }
19150         
19151         // remove - but keep children..
19152         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19153             while (node.childNodes.length) {
19154                 var cn = node.childNodes[0];
19155                 node.removeChild(cn);
19156                 node.parentNode.insertBefore(cn, node);
19157             }
19158             node.parentNode.removeChild(node);
19159             cleanWordChildren();
19160             return;
19161         }
19162         // clean styles
19163         if (node.className.length) {
19164             
19165             var cn = node.className.split(/\W+/);
19166             var cna = [];
19167             Roo.each(cn, function(cls) {
19168                 if (cls.match(/Mso[a-zA-Z]+/)) {
19169                     return;
19170                 }
19171                 cna.push(cls);
19172             });
19173             node.className = cna.length ? cna.join(' ') : '';
19174             if (!cna.length) {
19175                 node.removeAttribute("class");
19176             }
19177         }
19178         
19179         if (node.hasAttribute("lang")) {
19180             node.removeAttribute("lang");
19181         }
19182         
19183         if (node.hasAttribute("style")) {
19184             
19185             var styles = node.getAttribute("style").split(";");
19186             var nstyle = [];
19187             Roo.each(styles, function(s) {
19188                 if (!s.match(/:/)) {
19189                     return;
19190                 }
19191                 var kv = s.split(":");
19192                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19193                     return;
19194                 }
19195                 // what ever is left... we allow.
19196                 nstyle.push(s);
19197             });
19198             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19199             if (!nstyle.length) {
19200                 node.removeAttribute('style');
19201             }
19202         }
19203         
19204         cleanWordChildren();
19205         
19206         
19207     },
19208     domToHTML : function(currentElement, depth, nopadtext) {
19209         
19210         depth = depth || 0;
19211         nopadtext = nopadtext || false;
19212     
19213         if (!currentElement) {
19214             return this.domToHTML(this.doc.body);
19215         }
19216         
19217         //Roo.log(currentElement);
19218         var j;
19219         var allText = false;
19220         var nodeName = currentElement.nodeName;
19221         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19222         
19223         if  (nodeName == '#text') {
19224             
19225             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19226         }
19227         
19228         
19229         var ret = '';
19230         if (nodeName != 'BODY') {
19231              
19232             var i = 0;
19233             // Prints the node tagName, such as <A>, <IMG>, etc
19234             if (tagName) {
19235                 var attr = [];
19236                 for(i = 0; i < currentElement.attributes.length;i++) {
19237                     // quoting?
19238                     var aname = currentElement.attributes.item(i).name;
19239                     if (!currentElement.attributes.item(i).value.length) {
19240                         continue;
19241                     }
19242                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19243                 }
19244                 
19245                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19246             } 
19247             else {
19248                 
19249                 // eack
19250             }
19251         } else {
19252             tagName = false;
19253         }
19254         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19255             return ret;
19256         }
19257         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19258             nopadtext = true;
19259         }
19260         
19261         
19262         // Traverse the tree
19263         i = 0;
19264         var currentElementChild = currentElement.childNodes.item(i);
19265         var allText = true;
19266         var innerHTML  = '';
19267         lastnode = '';
19268         while (currentElementChild) {
19269             // Formatting code (indent the tree so it looks nice on the screen)
19270             var nopad = nopadtext;
19271             if (lastnode == 'SPAN') {
19272                 nopad  = true;
19273             }
19274             // text
19275             if  (currentElementChild.nodeName == '#text') {
19276                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19277                 toadd = nopadtext ? toadd : toadd.trim();
19278                 if (!nopad && toadd.length > 80) {
19279                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19280                 }
19281                 innerHTML  += toadd;
19282                 
19283                 i++;
19284                 currentElementChild = currentElement.childNodes.item(i);
19285                 lastNode = '';
19286                 continue;
19287             }
19288             allText = false;
19289             
19290             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19291                 
19292             // Recursively traverse the tree structure of the child node
19293             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19294             lastnode = currentElementChild.nodeName;
19295             i++;
19296             currentElementChild=currentElement.childNodes.item(i);
19297         }
19298         
19299         ret += innerHTML;
19300         
19301         if (!allText) {
19302                 // The remaining code is mostly for formatting the tree
19303             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19304         }
19305         
19306         
19307         if (tagName) {
19308             ret+= "</"+tagName+">";
19309         }
19310         return ret;
19311         
19312     },
19313         
19314     applyBlacklists : function()
19315     {
19316         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19317         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19318         
19319         this.white = [];
19320         this.black = [];
19321         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19322             if (b.indexOf(tag) > -1) {
19323                 return;
19324             }
19325             this.white.push(tag);
19326             
19327         }, this);
19328         
19329         Roo.each(w, function(tag) {
19330             if (b.indexOf(tag) > -1) {
19331                 return;
19332             }
19333             if (this.white.indexOf(tag) > -1) {
19334                 return;
19335             }
19336             this.white.push(tag);
19337             
19338         }, this);
19339         
19340         
19341         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19342             if (w.indexOf(tag) > -1) {
19343                 return;
19344             }
19345             this.black.push(tag);
19346             
19347         }, this);
19348         
19349         Roo.each(b, function(tag) {
19350             if (w.indexOf(tag) > -1) {
19351                 return;
19352             }
19353             if (this.black.indexOf(tag) > -1) {
19354                 return;
19355             }
19356             this.black.push(tag);
19357             
19358         }, this);
19359         
19360         
19361         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19362         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19363         
19364         this.cwhite = [];
19365         this.cblack = [];
19366         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19367             if (b.indexOf(tag) > -1) {
19368                 return;
19369             }
19370             this.cwhite.push(tag);
19371             
19372         }, this);
19373         
19374         Roo.each(w, function(tag) {
19375             if (b.indexOf(tag) > -1) {
19376                 return;
19377             }
19378             if (this.cwhite.indexOf(tag) > -1) {
19379                 return;
19380             }
19381             this.cwhite.push(tag);
19382             
19383         }, this);
19384         
19385         
19386         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19387             if (w.indexOf(tag) > -1) {
19388                 return;
19389             }
19390             this.cblack.push(tag);
19391             
19392         }, this);
19393         
19394         Roo.each(b, function(tag) {
19395             if (w.indexOf(tag) > -1) {
19396                 return;
19397             }
19398             if (this.cblack.indexOf(tag) > -1) {
19399                 return;
19400             }
19401             this.cblack.push(tag);
19402             
19403         }, this);
19404     },
19405     
19406     setStylesheets : function(stylesheets)
19407     {
19408         if(typeof(stylesheets) == 'string'){
19409             Roo.get(this.iframe.contentDocument.head).createChild({
19410                 tag : 'link',
19411                 rel : 'stylesheet',
19412                 type : 'text/css',
19413                 href : stylesheets
19414             });
19415             
19416             return;
19417         }
19418         var _this = this;
19419      
19420         Roo.each(stylesheets, function(s) {
19421             if(!s.length){
19422                 return;
19423             }
19424             
19425             Roo.get(_this.iframe.contentDocument.head).createChild({
19426                 tag : 'link',
19427                 rel : 'stylesheet',
19428                 type : 'text/css',
19429                 href : s
19430             });
19431         });
19432
19433         
19434     },
19435     
19436     removeStylesheets : function()
19437     {
19438         var _this = this;
19439         
19440         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19441             s.remove();
19442         });
19443     }
19444     
19445     // hide stuff that is not compatible
19446     /**
19447      * @event blur
19448      * @hide
19449      */
19450     /**
19451      * @event change
19452      * @hide
19453      */
19454     /**
19455      * @event focus
19456      * @hide
19457      */
19458     /**
19459      * @event specialkey
19460      * @hide
19461      */
19462     /**
19463      * @cfg {String} fieldClass @hide
19464      */
19465     /**
19466      * @cfg {String} focusClass @hide
19467      */
19468     /**
19469      * @cfg {String} autoCreate @hide
19470      */
19471     /**
19472      * @cfg {String} inputType @hide
19473      */
19474     /**
19475      * @cfg {String} invalidClass @hide
19476      */
19477     /**
19478      * @cfg {String} invalidText @hide
19479      */
19480     /**
19481      * @cfg {String} msgFx @hide
19482      */
19483     /**
19484      * @cfg {String} validateOnBlur @hide
19485      */
19486 });
19487
19488 Roo.HtmlEditorCore.white = [
19489         'area', 'br', 'img', 'input', 'hr', 'wbr',
19490         
19491        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19492        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19493        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19494        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19495        'table',   'ul',         'xmp', 
19496        
19497        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19498       'thead',   'tr', 
19499      
19500       'dir', 'menu', 'ol', 'ul', 'dl',
19501        
19502       'embed',  'object'
19503 ];
19504
19505
19506 Roo.HtmlEditorCore.black = [
19507     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19508         'applet', // 
19509         'base',   'basefont', 'bgsound', 'blink',  'body', 
19510         'frame',  'frameset', 'head',    'html',   'ilayer', 
19511         'iframe', 'layer',  'link',     'meta',    'object',   
19512         'script', 'style' ,'title',  'xml' // clean later..
19513 ];
19514 Roo.HtmlEditorCore.clean = [
19515     'script', 'style', 'title', 'xml'
19516 ];
19517 Roo.HtmlEditorCore.remove = [
19518     'font'
19519 ];
19520 // attributes..
19521
19522 Roo.HtmlEditorCore.ablack = [
19523     'on'
19524 ];
19525     
19526 Roo.HtmlEditorCore.aclean = [ 
19527     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19528 ];
19529
19530 // protocols..
19531 Roo.HtmlEditorCore.pwhite= [
19532         'http',  'https',  'mailto'
19533 ];
19534
19535 // white listed style attributes.
19536 Roo.HtmlEditorCore.cwhite= [
19537       //  'text-align', /// default is to allow most things..
19538       
19539          
19540 //        'font-size'//??
19541 ];
19542
19543 // black listed style attributes.
19544 Roo.HtmlEditorCore.cblack= [
19545       //  'font-size' -- this can be set by the project 
19546 ];
19547
19548
19549 Roo.HtmlEditorCore.swapCodes   =[ 
19550     [    8211, "--" ], 
19551     [    8212, "--" ], 
19552     [    8216,  "'" ],  
19553     [    8217, "'" ],  
19554     [    8220, '"' ],  
19555     [    8221, '"' ],  
19556     [    8226, "*" ],  
19557     [    8230, "..." ]
19558 ]; 
19559
19560     /*
19561  * - LGPL
19562  *
19563  * HtmlEditor
19564  * 
19565  */
19566
19567 /**
19568  * @class Roo.bootstrap.HtmlEditor
19569  * @extends Roo.bootstrap.TextArea
19570  * Bootstrap HtmlEditor class
19571
19572  * @constructor
19573  * Create a new HtmlEditor
19574  * @param {Object} config The config object
19575  */
19576
19577 Roo.bootstrap.HtmlEditor = function(config){
19578     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19579     if (!this.toolbars) {
19580         this.toolbars = [];
19581     }
19582     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19583     this.addEvents({
19584             /**
19585              * @event initialize
19586              * Fires when the editor is fully initialized (including the iframe)
19587              * @param {HtmlEditor} this
19588              */
19589             initialize: true,
19590             /**
19591              * @event activate
19592              * Fires when the editor is first receives the focus. Any insertion must wait
19593              * until after this event.
19594              * @param {HtmlEditor} this
19595              */
19596             activate: true,
19597              /**
19598              * @event beforesync
19599              * Fires before the textarea is updated with content from the editor iframe. Return false
19600              * to cancel the sync.
19601              * @param {HtmlEditor} this
19602              * @param {String} html
19603              */
19604             beforesync: true,
19605              /**
19606              * @event beforepush
19607              * Fires before the iframe editor is updated with content from the textarea. Return false
19608              * to cancel the push.
19609              * @param {HtmlEditor} this
19610              * @param {String} html
19611              */
19612             beforepush: true,
19613              /**
19614              * @event sync
19615              * Fires when the textarea is updated with content from the editor iframe.
19616              * @param {HtmlEditor} this
19617              * @param {String} html
19618              */
19619             sync: true,
19620              /**
19621              * @event push
19622              * Fires when the iframe editor is updated with content from the textarea.
19623              * @param {HtmlEditor} this
19624              * @param {String} html
19625              */
19626             push: true,
19627              /**
19628              * @event editmodechange
19629              * Fires when the editor switches edit modes
19630              * @param {HtmlEditor} this
19631              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19632              */
19633             editmodechange: true,
19634             /**
19635              * @event editorevent
19636              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19637              * @param {HtmlEditor} this
19638              */
19639             editorevent: true,
19640             /**
19641              * @event firstfocus
19642              * Fires when on first focus - needed by toolbars..
19643              * @param {HtmlEditor} this
19644              */
19645             firstfocus: true,
19646             /**
19647              * @event autosave
19648              * Auto save the htmlEditor value as a file into Events
19649              * @param {HtmlEditor} this
19650              */
19651             autosave: true,
19652             /**
19653              * @event savedpreview
19654              * preview the saved version of htmlEditor
19655              * @param {HtmlEditor} this
19656              */
19657             savedpreview: true
19658         });
19659 };
19660
19661
19662 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19663     
19664     
19665       /**
19666      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19667      */
19668     toolbars : false,
19669    
19670      /**
19671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19672      *                        Roo.resizable.
19673      */
19674     resizable : false,
19675      /**
19676      * @cfg {Number} height (in pixels)
19677      */   
19678     height: 300,
19679    /**
19680      * @cfg {Number} width (in pixels)
19681      */   
19682     width: false,
19683     
19684     /**
19685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19686      * 
19687      */
19688     stylesheets: false,
19689     
19690     // id of frame..
19691     frameId: false,
19692     
19693     // private properties
19694     validationEvent : false,
19695     deferHeight: true,
19696     initialized : false,
19697     activated : false,
19698     
19699     onFocus : Roo.emptyFn,
19700     iframePad:3,
19701     hideMode:'offsets',
19702     
19703     
19704     tbContainer : false,
19705     
19706     toolbarContainer :function() {
19707         return this.wrap.select('.x-html-editor-tb',true).first();
19708     },
19709
19710     /**
19711      * Protected method that will not generally be called directly. It
19712      * is called when the editor creates its toolbar. Override this method if you need to
19713      * add custom toolbar buttons.
19714      * @param {HtmlEditor} editor
19715      */
19716     createToolbar : function(){
19717         
19718         Roo.log("create toolbars");
19719         
19720         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19721         this.toolbars[0].render(this.toolbarContainer());
19722         
19723         return;
19724         
19725 //        if (!editor.toolbars || !editor.toolbars.length) {
19726 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19727 //        }
19728 //        
19729 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19730 //            editor.toolbars[i] = Roo.factory(
19731 //                    typeof(editor.toolbars[i]) == 'string' ?
19732 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19733 //                Roo.bootstrap.HtmlEditor);
19734 //            editor.toolbars[i].init(editor);
19735 //        }
19736     },
19737
19738      
19739     // private
19740     onRender : function(ct, position)
19741     {
19742        // Roo.log("Call onRender: " + this.xtype);
19743         var _t = this;
19744         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19745       
19746         this.wrap = this.inputEl().wrap({
19747             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19748         });
19749         
19750         this.editorcore.onRender(ct, position);
19751          
19752         if (this.resizable) {
19753             this.resizeEl = new Roo.Resizable(this.wrap, {
19754                 pinned : true,
19755                 wrap: true,
19756                 dynamic : true,
19757                 minHeight : this.height,
19758                 height: this.height,
19759                 handles : this.resizable,
19760                 width: this.width,
19761                 listeners : {
19762                     resize : function(r, w, h) {
19763                         _t.onResize(w,h); // -something
19764                     }
19765                 }
19766             });
19767             
19768         }
19769         this.createToolbar(this);
19770        
19771         
19772         if(!this.width && this.resizable){
19773             this.setSize(this.wrap.getSize());
19774         }
19775         if (this.resizeEl) {
19776             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19777             // should trigger onReize..
19778         }
19779         
19780     },
19781
19782     // private
19783     onResize : function(w, h)
19784     {
19785         Roo.log('resize: ' +w + ',' + h );
19786         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19787         var ew = false;
19788         var eh = false;
19789         
19790         if(this.inputEl() ){
19791             if(typeof w == 'number'){
19792                 var aw = w - this.wrap.getFrameWidth('lr');
19793                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19794                 ew = aw;
19795             }
19796             if(typeof h == 'number'){
19797                  var tbh = -11;  // fixme it needs to tool bar size!
19798                 for (var i =0; i < this.toolbars.length;i++) {
19799                     // fixme - ask toolbars for heights?
19800                     tbh += this.toolbars[i].el.getHeight();
19801                     //if (this.toolbars[i].footer) {
19802                     //    tbh += this.toolbars[i].footer.el.getHeight();
19803                     //}
19804                 }
19805               
19806                 
19807                 
19808                 
19809                 
19810                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19811                 ah -= 5; // knock a few pixes off for look..
19812                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19813                 var eh = ah;
19814             }
19815         }
19816         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19817         this.editorcore.onResize(ew,eh);
19818         
19819     },
19820
19821     /**
19822      * Toggles the editor between standard and source edit mode.
19823      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19824      */
19825     toggleSourceEdit : function(sourceEditMode)
19826     {
19827         this.editorcore.toggleSourceEdit(sourceEditMode);
19828         
19829         if(this.editorcore.sourceEditMode){
19830             Roo.log('editor - showing textarea');
19831             
19832 //            Roo.log('in');
19833 //            Roo.log(this.syncValue());
19834             this.syncValue();
19835             this.inputEl().removeClass(['hide', 'x-hidden']);
19836             this.inputEl().dom.removeAttribute('tabIndex');
19837             this.inputEl().focus();
19838         }else{
19839             Roo.log('editor - hiding textarea');
19840 //            Roo.log('out')
19841 //            Roo.log(this.pushValue()); 
19842             this.pushValue();
19843             
19844             this.inputEl().addClass(['hide', 'x-hidden']);
19845             this.inputEl().dom.setAttribute('tabIndex', -1);
19846             //this.deferFocus();
19847         }
19848          
19849         if(this.resizable){
19850             this.setSize(this.wrap.getSize());
19851         }
19852         
19853         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19854     },
19855  
19856     // private (for BoxComponent)
19857     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19858
19859     // private (for BoxComponent)
19860     getResizeEl : function(){
19861         return this.wrap;
19862     },
19863
19864     // private (for BoxComponent)
19865     getPositionEl : function(){
19866         return this.wrap;
19867     },
19868
19869     // private
19870     initEvents : function(){
19871         this.originalValue = this.getValue();
19872     },
19873
19874 //    /**
19875 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19876 //     * @method
19877 //     */
19878 //    markInvalid : Roo.emptyFn,
19879 //    /**
19880 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19881 //     * @method
19882 //     */
19883 //    clearInvalid : Roo.emptyFn,
19884
19885     setValue : function(v){
19886         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19887         this.editorcore.pushValue();
19888     },
19889
19890      
19891     // private
19892     deferFocus : function(){
19893         this.focus.defer(10, this);
19894     },
19895
19896     // doc'ed in Field
19897     focus : function(){
19898         this.editorcore.focus();
19899         
19900     },
19901       
19902
19903     // private
19904     onDestroy : function(){
19905         
19906         
19907         
19908         if(this.rendered){
19909             
19910             for (var i =0; i < this.toolbars.length;i++) {
19911                 // fixme - ask toolbars for heights?
19912                 this.toolbars[i].onDestroy();
19913             }
19914             
19915             this.wrap.dom.innerHTML = '';
19916             this.wrap.remove();
19917         }
19918     },
19919
19920     // private
19921     onFirstFocus : function(){
19922         //Roo.log("onFirstFocus");
19923         this.editorcore.onFirstFocus();
19924          for (var i =0; i < this.toolbars.length;i++) {
19925             this.toolbars[i].onFirstFocus();
19926         }
19927         
19928     },
19929     
19930     // private
19931     syncValue : function()
19932     {   
19933         this.editorcore.syncValue();
19934     },
19935     
19936     pushValue : function()
19937     {   
19938         this.editorcore.pushValue();
19939     }
19940      
19941     
19942     // hide stuff that is not compatible
19943     /**
19944      * @event blur
19945      * @hide
19946      */
19947     /**
19948      * @event change
19949      * @hide
19950      */
19951     /**
19952      * @event focus
19953      * @hide
19954      */
19955     /**
19956      * @event specialkey
19957      * @hide
19958      */
19959     /**
19960      * @cfg {String} fieldClass @hide
19961      */
19962     /**
19963      * @cfg {String} focusClass @hide
19964      */
19965     /**
19966      * @cfg {String} autoCreate @hide
19967      */
19968     /**
19969      * @cfg {String} inputType @hide
19970      */
19971     /**
19972      * @cfg {String} invalidClass @hide
19973      */
19974     /**
19975      * @cfg {String} invalidText @hide
19976      */
19977     /**
19978      * @cfg {String} msgFx @hide
19979      */
19980     /**
19981      * @cfg {String} validateOnBlur @hide
19982      */
19983 });
19984  
19985     
19986    
19987    
19988    
19989       
19990 Roo.namespace('Roo.bootstrap.htmleditor');
19991 /**
19992  * @class Roo.bootstrap.HtmlEditorToolbar1
19993  * Basic Toolbar
19994  * 
19995  * Usage:
19996  *
19997  new Roo.bootstrap.HtmlEditor({
19998     ....
19999     toolbars : [
20000         new Roo.bootstrap.HtmlEditorToolbar1({
20001             disable : { fonts: 1 , format: 1, ..., ... , ...],
20002             btns : [ .... ]
20003         })
20004     }
20005      
20006  * 
20007  * @cfg {Object} disable List of elements to disable..
20008  * @cfg {Array} btns List of additional buttons.
20009  * 
20010  * 
20011  * NEEDS Extra CSS? 
20012  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20013  */
20014  
20015 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20016 {
20017     
20018     Roo.apply(this, config);
20019     
20020     // default disabled, based on 'good practice'..
20021     this.disable = this.disable || {};
20022     Roo.applyIf(this.disable, {
20023         fontSize : true,
20024         colors : true,
20025         specialElements : true
20026     });
20027     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20028     
20029     this.editor = config.editor;
20030     this.editorcore = config.editor.editorcore;
20031     
20032     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20033     
20034     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20035     // dont call parent... till later.
20036 }
20037 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20038      
20039     bar : true,
20040     
20041     editor : false,
20042     editorcore : false,
20043     
20044     
20045     formats : [
20046         "p" ,  
20047         "h1","h2","h3","h4","h5","h6", 
20048         "pre", "code", 
20049         "abbr", "acronym", "address", "cite", "samp", "var",
20050         'div','span'
20051     ],
20052     
20053     onRender : function(ct, position)
20054     {
20055        // Roo.log("Call onRender: " + this.xtype);
20056         
20057        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20058        Roo.log(this.el);
20059        this.el.dom.style.marginBottom = '0';
20060        var _this = this;
20061        var editorcore = this.editorcore;
20062        var editor= this.editor;
20063        
20064        var children = [];
20065        var btn = function(id,cmd , toggle, handler){
20066        
20067             var  event = toggle ? 'toggle' : 'click';
20068        
20069             var a = {
20070                 size : 'sm',
20071                 xtype: 'Button',
20072                 xns: Roo.bootstrap,
20073                 glyphicon : id,
20074                 cmd : id || cmd,
20075                 enableToggle:toggle !== false,
20076                 //html : 'submit'
20077                 pressed : toggle ? false : null,
20078                 listeners : {}
20079             }
20080             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20081                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20082             }
20083             children.push(a);
20084             return a;
20085        }
20086         
20087         var style = {
20088                 xtype: 'Button',
20089                 size : 'sm',
20090                 xns: Roo.bootstrap,
20091                 glyphicon : 'font',
20092                 //html : 'submit'
20093                 menu : {
20094                     xtype: 'Menu',
20095                     xns: Roo.bootstrap,
20096                     items:  []
20097                 }
20098         };
20099         Roo.each(this.formats, function(f) {
20100             style.menu.items.push({
20101                 xtype :'MenuItem',
20102                 xns: Roo.bootstrap,
20103                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20104                 tagname : f,
20105                 listeners : {
20106                     click : function()
20107                     {
20108                         editorcore.insertTag(this.tagname);
20109                         editor.focus();
20110                     }
20111                 }
20112                 
20113             });
20114         });
20115          children.push(style);   
20116             
20117             
20118         btn('bold',false,true);
20119         btn('italic',false,true);
20120         btn('align-left', 'justifyleft',true);
20121         btn('align-center', 'justifycenter',true);
20122         btn('align-right' , 'justifyright',true);
20123         btn('link', false, false, function(btn) {
20124             //Roo.log("create link?");
20125             var url = prompt(this.createLinkText, this.defaultLinkValue);
20126             if(url && url != 'http:/'+'/'){
20127                 this.editorcore.relayCmd('createlink', url);
20128             }
20129         }),
20130         btn('list','insertunorderedlist',true);
20131         btn('pencil', false,true, function(btn){
20132                 Roo.log(this);
20133                 
20134                 this.toggleSourceEdit(btn.pressed);
20135         });
20136         /*
20137         var cog = {
20138                 xtype: 'Button',
20139                 size : 'sm',
20140                 xns: Roo.bootstrap,
20141                 glyphicon : 'cog',
20142                 //html : 'submit'
20143                 menu : {
20144                     xtype: 'Menu',
20145                     xns: Roo.bootstrap,
20146                     items:  []
20147                 }
20148         };
20149         
20150         cog.menu.items.push({
20151             xtype :'MenuItem',
20152             xns: Roo.bootstrap,
20153             html : Clean styles,
20154             tagname : f,
20155             listeners : {
20156                 click : function()
20157                 {
20158                     editorcore.insertTag(this.tagname);
20159                     editor.focus();
20160                 }
20161             }
20162             
20163         });
20164        */
20165         
20166          
20167        this.xtype = 'NavSimplebar';
20168         
20169         for(var i=0;i< children.length;i++) {
20170             
20171             this.buttons.add(this.addxtypeChild(children[i]));
20172             
20173         }
20174         
20175         editor.on('editorevent', this.updateToolbar, this);
20176     },
20177     onBtnClick : function(id)
20178     {
20179        this.editorcore.relayCmd(id);
20180        this.editorcore.focus();
20181     },
20182     
20183     /**
20184      * Protected method that will not generally be called directly. It triggers
20185      * a toolbar update by reading the markup state of the current selection in the editor.
20186      */
20187     updateToolbar: function(){
20188
20189         if(!this.editorcore.activated){
20190             this.editor.onFirstFocus(); // is this neeed?
20191             return;
20192         }
20193
20194         var btns = this.buttons; 
20195         var doc = this.editorcore.doc;
20196         btns.get('bold').setActive(doc.queryCommandState('bold'));
20197         btns.get('italic').setActive(doc.queryCommandState('italic'));
20198         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20199         
20200         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20201         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20202         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20203         
20204         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20205         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20206          /*
20207         
20208         var ans = this.editorcore.getAllAncestors();
20209         if (this.formatCombo) {
20210             
20211             
20212             var store = this.formatCombo.store;
20213             this.formatCombo.setValue("");
20214             for (var i =0; i < ans.length;i++) {
20215                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20216                     // select it..
20217                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20218                     break;
20219                 }
20220             }
20221         }
20222         
20223         
20224         
20225         // hides menus... - so this cant be on a menu...
20226         Roo.bootstrap.MenuMgr.hideAll();
20227         */
20228         Roo.bootstrap.MenuMgr.hideAll();
20229         //this.editorsyncValue();
20230     },
20231     onFirstFocus: function() {
20232         this.buttons.each(function(item){
20233            item.enable();
20234         });
20235     },
20236     toggleSourceEdit : function(sourceEditMode){
20237         
20238           
20239         if(sourceEditMode){
20240             Roo.log("disabling buttons");
20241            this.buttons.each( function(item){
20242                 if(item.cmd != 'pencil'){
20243                     item.disable();
20244                 }
20245             });
20246           
20247         }else{
20248             Roo.log("enabling buttons");
20249             if(this.editorcore.initialized){
20250                 this.buttons.each( function(item){
20251                     item.enable();
20252                 });
20253             }
20254             
20255         }
20256         Roo.log("calling toggole on editor");
20257         // tell the editor that it's been pressed..
20258         this.editor.toggleSourceEdit(sourceEditMode);
20259        
20260     }
20261 });
20262
20263
20264
20265
20266
20267 /**
20268  * @class Roo.bootstrap.Table.AbstractSelectionModel
20269  * @extends Roo.util.Observable
20270  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20271  * implemented by descendant classes.  This class should not be directly instantiated.
20272  * @constructor
20273  */
20274 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20275     this.locked = false;
20276     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20277 };
20278
20279
20280 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20281     /** @ignore Called by the grid automatically. Do not call directly. */
20282     init : function(grid){
20283         this.grid = grid;
20284         this.initEvents();
20285     },
20286
20287     /**
20288      * Locks the selections.
20289      */
20290     lock : function(){
20291         this.locked = true;
20292     },
20293
20294     /**
20295      * Unlocks the selections.
20296      */
20297     unlock : function(){
20298         this.locked = false;
20299     },
20300
20301     /**
20302      * Returns true if the selections are locked.
20303      * @return {Boolean}
20304      */
20305     isLocked : function(){
20306         return this.locked;
20307     }
20308 });
20309 /**
20310  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20311  * @class Roo.bootstrap.Table.RowSelectionModel
20312  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20313  * It supports multiple selections and keyboard selection/navigation. 
20314  * @constructor
20315  * @param {Object} config
20316  */
20317
20318 Roo.bootstrap.Table.RowSelectionModel = function(config){
20319     Roo.apply(this, config);
20320     this.selections = new Roo.util.MixedCollection(false, function(o){
20321         return o.id;
20322     });
20323
20324     this.last = false;
20325     this.lastActive = false;
20326
20327     this.addEvents({
20328         /**
20329              * @event selectionchange
20330              * Fires when the selection changes
20331              * @param {SelectionModel} this
20332              */
20333             "selectionchange" : true,
20334         /**
20335              * @event afterselectionchange
20336              * Fires after the selection changes (eg. by key press or clicking)
20337              * @param {SelectionModel} this
20338              */
20339             "afterselectionchange" : true,
20340         /**
20341              * @event beforerowselect
20342              * Fires when a row is selected being selected, return false to cancel.
20343              * @param {SelectionModel} this
20344              * @param {Number} rowIndex The selected index
20345              * @param {Boolean} keepExisting False if other selections will be cleared
20346              */
20347             "beforerowselect" : true,
20348         /**
20349              * @event rowselect
20350              * Fires when a row is selected.
20351              * @param {SelectionModel} this
20352              * @param {Number} rowIndex The selected index
20353              * @param {Roo.data.Record} r The record
20354              */
20355             "rowselect" : true,
20356         /**
20357              * @event rowdeselect
20358              * Fires when a row is deselected.
20359              * @param {SelectionModel} this
20360              * @param {Number} rowIndex The selected index
20361              */
20362         "rowdeselect" : true
20363     });
20364     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20365     this.locked = false;
20366 };
20367
20368 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20369     /**
20370      * @cfg {Boolean} singleSelect
20371      * True to allow selection of only one row at a time (defaults to false)
20372      */
20373     singleSelect : false,
20374
20375     // private
20376     initEvents : function(){
20377
20378         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20379             this.grid.on("mousedown", this.handleMouseDown, this);
20380         }else{ // allow click to work like normal
20381             this.grid.on("rowclick", this.handleDragableRowClick, this);
20382         }
20383
20384         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20385             "up" : function(e){
20386                 if(!e.shiftKey){
20387                     this.selectPrevious(e.shiftKey);
20388                 }else if(this.last !== false && this.lastActive !== false){
20389                     var last = this.last;
20390                     this.selectRange(this.last,  this.lastActive-1);
20391                     this.grid.getView().focusRow(this.lastActive);
20392                     if(last !== false){
20393                         this.last = last;
20394                     }
20395                 }else{
20396                     this.selectFirstRow();
20397                 }
20398                 this.fireEvent("afterselectionchange", this);
20399             },
20400             "down" : function(e){
20401                 if(!e.shiftKey){
20402                     this.selectNext(e.shiftKey);
20403                 }else if(this.last !== false && this.lastActive !== false){
20404                     var last = this.last;
20405                     this.selectRange(this.last,  this.lastActive+1);
20406                     this.grid.getView().focusRow(this.lastActive);
20407                     if(last !== false){
20408                         this.last = last;
20409                     }
20410                 }else{
20411                     this.selectFirstRow();
20412                 }
20413                 this.fireEvent("afterselectionchange", this);
20414             },
20415             scope: this
20416         });
20417
20418         var view = this.grid.view;
20419         view.on("refresh", this.onRefresh, this);
20420         view.on("rowupdated", this.onRowUpdated, this);
20421         view.on("rowremoved", this.onRemove, this);
20422     },
20423
20424     // private
20425     onRefresh : function(){
20426         var ds = this.grid.dataSource, i, v = this.grid.view;
20427         var s = this.selections;
20428         s.each(function(r){
20429             if((i = ds.indexOfId(r.id)) != -1){
20430                 v.onRowSelect(i);
20431             }else{
20432                 s.remove(r);
20433             }
20434         });
20435     },
20436
20437     // private
20438     onRemove : function(v, index, r){
20439         this.selections.remove(r);
20440     },
20441
20442     // private
20443     onRowUpdated : function(v, index, r){
20444         if(this.isSelected(r)){
20445             v.onRowSelect(index);
20446         }
20447     },
20448
20449     /**
20450      * Select records.
20451      * @param {Array} records The records to select
20452      * @param {Boolean} keepExisting (optional) True to keep existing selections
20453      */
20454     selectRecords : function(records, keepExisting){
20455         if(!keepExisting){
20456             this.clearSelections();
20457         }
20458         var ds = this.grid.dataSource;
20459         for(var i = 0, len = records.length; i < len; i++){
20460             this.selectRow(ds.indexOf(records[i]), true);
20461         }
20462     },
20463
20464     /**
20465      * Gets the number of selected rows.
20466      * @return {Number}
20467      */
20468     getCount : function(){
20469         return this.selections.length;
20470     },
20471
20472     /**
20473      * Selects the first row in the grid.
20474      */
20475     selectFirstRow : function(){
20476         this.selectRow(0);
20477     },
20478
20479     /**
20480      * Select the last row.
20481      * @param {Boolean} keepExisting (optional) True to keep existing selections
20482      */
20483     selectLastRow : function(keepExisting){
20484         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20485     },
20486
20487     /**
20488      * Selects the row immediately following the last selected row.
20489      * @param {Boolean} keepExisting (optional) True to keep existing selections
20490      */
20491     selectNext : function(keepExisting){
20492         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20493             this.selectRow(this.last+1, keepExisting);
20494             this.grid.getView().focusRow(this.last);
20495         }
20496     },
20497
20498     /**
20499      * Selects the row that precedes the last selected row.
20500      * @param {Boolean} keepExisting (optional) True to keep existing selections
20501      */
20502     selectPrevious : function(keepExisting){
20503         if(this.last){
20504             this.selectRow(this.last-1, keepExisting);
20505             this.grid.getView().focusRow(this.last);
20506         }
20507     },
20508
20509     /**
20510      * Returns the selected records
20511      * @return {Array} Array of selected records
20512      */
20513     getSelections : function(){
20514         return [].concat(this.selections.items);
20515     },
20516
20517     /**
20518      * Returns the first selected record.
20519      * @return {Record}
20520      */
20521     getSelected : function(){
20522         return this.selections.itemAt(0);
20523     },
20524
20525
20526     /**
20527      * Clears all selections.
20528      */
20529     clearSelections : function(fast){
20530         if(this.locked) return;
20531         if(fast !== true){
20532             var ds = this.grid.dataSource;
20533             var s = this.selections;
20534             s.each(function(r){
20535                 this.deselectRow(ds.indexOfId(r.id));
20536             }, this);
20537             s.clear();
20538         }else{
20539             this.selections.clear();
20540         }
20541         this.last = false;
20542     },
20543
20544
20545     /**
20546      * Selects all rows.
20547      */
20548     selectAll : function(){
20549         if(this.locked) return;
20550         this.selections.clear();
20551         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20552             this.selectRow(i, true);
20553         }
20554     },
20555
20556     /**
20557      * Returns True if there is a selection.
20558      * @return {Boolean}
20559      */
20560     hasSelection : function(){
20561         return this.selections.length > 0;
20562     },
20563
20564     /**
20565      * Returns True if the specified row is selected.
20566      * @param {Number/Record} record The record or index of the record to check
20567      * @return {Boolean}
20568      */
20569     isSelected : function(index){
20570         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20571         return (r && this.selections.key(r.id) ? true : false);
20572     },
20573
20574     /**
20575      * Returns True if the specified record id is selected.
20576      * @param {String} id The id of record to check
20577      * @return {Boolean}
20578      */
20579     isIdSelected : function(id){
20580         return (this.selections.key(id) ? true : false);
20581     },
20582
20583     // private
20584     handleMouseDown : function(e, t){
20585         var view = this.grid.getView(), rowIndex;
20586         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20587             return;
20588         };
20589         if(e.shiftKey && this.last !== false){
20590             var last = this.last;
20591             this.selectRange(last, rowIndex, e.ctrlKey);
20592             this.last = last; // reset the last
20593             view.focusRow(rowIndex);
20594         }else{
20595             var isSelected = this.isSelected(rowIndex);
20596             if(e.button !== 0 && isSelected){
20597                 view.focusRow(rowIndex);
20598             }else if(e.ctrlKey && isSelected){
20599                 this.deselectRow(rowIndex);
20600             }else if(!isSelected){
20601                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20602                 view.focusRow(rowIndex);
20603             }
20604         }
20605         this.fireEvent("afterselectionchange", this);
20606     },
20607     // private
20608     handleDragableRowClick :  function(grid, rowIndex, e) 
20609     {
20610         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20611             this.selectRow(rowIndex, false);
20612             grid.view.focusRow(rowIndex);
20613              this.fireEvent("afterselectionchange", this);
20614         }
20615     },
20616     
20617     /**
20618      * Selects multiple rows.
20619      * @param {Array} rows Array of the indexes of the row to select
20620      * @param {Boolean} keepExisting (optional) True to keep existing selections
20621      */
20622     selectRows : function(rows, keepExisting){
20623         if(!keepExisting){
20624             this.clearSelections();
20625         }
20626         for(var i = 0, len = rows.length; i < len; i++){
20627             this.selectRow(rows[i], true);
20628         }
20629     },
20630
20631     /**
20632      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20633      * @param {Number} startRow The index of the first row in the range
20634      * @param {Number} endRow The index of the last row in the range
20635      * @param {Boolean} keepExisting (optional) True to retain existing selections
20636      */
20637     selectRange : function(startRow, endRow, keepExisting){
20638         if(this.locked) return;
20639         if(!keepExisting){
20640             this.clearSelections();
20641         }
20642         if(startRow <= endRow){
20643             for(var i = startRow; i <= endRow; i++){
20644                 this.selectRow(i, true);
20645             }
20646         }else{
20647             for(var i = startRow; i >= endRow; i--){
20648                 this.selectRow(i, true);
20649             }
20650         }
20651     },
20652
20653     /**
20654      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20655      * @param {Number} startRow The index of the first row in the range
20656      * @param {Number} endRow The index of the last row in the range
20657      */
20658     deselectRange : function(startRow, endRow, preventViewNotify){
20659         if(this.locked) return;
20660         for(var i = startRow; i <= endRow; i++){
20661             this.deselectRow(i, preventViewNotify);
20662         }
20663     },
20664
20665     /**
20666      * Selects a row.
20667      * @param {Number} row The index of the row to select
20668      * @param {Boolean} keepExisting (optional) True to keep existing selections
20669      */
20670     selectRow : function(index, keepExisting, preventViewNotify){
20671         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20672         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20673             if(!keepExisting || this.singleSelect){
20674                 this.clearSelections();
20675             }
20676             var r = this.grid.dataSource.getAt(index);
20677             this.selections.add(r);
20678             this.last = this.lastActive = index;
20679             if(!preventViewNotify){
20680                 this.grid.getView().onRowSelect(index);
20681             }
20682             this.fireEvent("rowselect", this, index, r);
20683             this.fireEvent("selectionchange", this);
20684         }
20685     },
20686
20687     /**
20688      * Deselects a row.
20689      * @param {Number} row The index of the row to deselect
20690      */
20691     deselectRow : function(index, preventViewNotify){
20692         if(this.locked) return;
20693         if(this.last == index){
20694             this.last = false;
20695         }
20696         if(this.lastActive == index){
20697             this.lastActive = false;
20698         }
20699         var r = this.grid.dataSource.getAt(index);
20700         this.selections.remove(r);
20701         if(!preventViewNotify){
20702             this.grid.getView().onRowDeselect(index);
20703         }
20704         this.fireEvent("rowdeselect", this, index);
20705         this.fireEvent("selectionchange", this);
20706     },
20707
20708     // private
20709     restoreLast : function(){
20710         if(this._last){
20711             this.last = this._last;
20712         }
20713     },
20714
20715     // private
20716     acceptsNav : function(row, col, cm){
20717         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20718     },
20719
20720     // private
20721     onEditorKey : function(field, e){
20722         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20723         if(k == e.TAB){
20724             e.stopEvent();
20725             ed.completeEdit();
20726             if(e.shiftKey){
20727                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20728             }else{
20729                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20730             }
20731         }else if(k == e.ENTER && !e.ctrlKey){
20732             e.stopEvent();
20733             ed.completeEdit();
20734             if(e.shiftKey){
20735                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20736             }else{
20737                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20738             }
20739         }else if(k == e.ESC){
20740             ed.cancelEdit();
20741         }
20742         if(newCell){
20743             g.startEditing(newCell[0], newCell[1]);
20744         }
20745     }
20746 });/*
20747  * Based on:
20748  * Ext JS Library 1.1.1
20749  * Copyright(c) 2006-2007, Ext JS, LLC.
20750  *
20751  * Originally Released Under LGPL - original licence link has changed is not relivant.
20752  *
20753  * Fork - LGPL
20754  * <script type="text/javascript">
20755  */
20756  
20757 /**
20758  * @class Roo.bootstrap.PagingToolbar
20759  * @extends Roo.Row
20760  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20761  * @constructor
20762  * Create a new PagingToolbar
20763  * @param {Object} config The config object
20764  */
20765 Roo.bootstrap.PagingToolbar = function(config)
20766 {
20767     // old args format still supported... - xtype is prefered..
20768         // created from xtype...
20769     var ds = config.dataSource;
20770     this.toolbarItems = [];
20771     if (config.items) {
20772         this.toolbarItems = config.items;
20773 //        config.items = [];
20774     }
20775     
20776     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20777     this.ds = ds;
20778     this.cursor = 0;
20779     if (ds) { 
20780         this.bind(ds);
20781     }
20782     
20783     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20784     
20785 };
20786
20787 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20788     /**
20789      * @cfg {Roo.data.Store} dataSource
20790      * The underlying data store providing the paged data
20791      */
20792     /**
20793      * @cfg {String/HTMLElement/Element} container
20794      * container The id or element that will contain the toolbar
20795      */
20796     /**
20797      * @cfg {Boolean} displayInfo
20798      * True to display the displayMsg (defaults to false)
20799      */
20800     /**
20801      * @cfg {Number} pageSize
20802      * The number of records to display per page (defaults to 20)
20803      */
20804     pageSize: 20,
20805     /**
20806      * @cfg {String} displayMsg
20807      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20808      */
20809     displayMsg : 'Displaying {0} - {1} of {2}',
20810     /**
20811      * @cfg {String} emptyMsg
20812      * The message to display when no records are found (defaults to "No data to display")
20813      */
20814     emptyMsg : 'No data to display',
20815     /**
20816      * Customizable piece of the default paging text (defaults to "Page")
20817      * @type String
20818      */
20819     beforePageText : "Page",
20820     /**
20821      * Customizable piece of the default paging text (defaults to "of %0")
20822      * @type String
20823      */
20824     afterPageText : "of {0}",
20825     /**
20826      * Customizable piece of the default paging text (defaults to "First Page")
20827      * @type String
20828      */
20829     firstText : "First Page",
20830     /**
20831      * Customizable piece of the default paging text (defaults to "Previous Page")
20832      * @type String
20833      */
20834     prevText : "Previous Page",
20835     /**
20836      * Customizable piece of the default paging text (defaults to "Next Page")
20837      * @type String
20838      */
20839     nextText : "Next Page",
20840     /**
20841      * Customizable piece of the default paging text (defaults to "Last Page")
20842      * @type String
20843      */
20844     lastText : "Last Page",
20845     /**
20846      * Customizable piece of the default paging text (defaults to "Refresh")
20847      * @type String
20848      */
20849     refreshText : "Refresh",
20850
20851     buttons : false,
20852     // private
20853     onRender : function(ct, position) 
20854     {
20855         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20856         this.navgroup.parentId = this.id;
20857         this.navgroup.onRender(this.el, null);
20858         // add the buttons to the navgroup
20859         
20860         if(this.displayInfo){
20861             Roo.log(this.el.select('ul.navbar-nav',true).first());
20862             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20863             this.displayEl = this.el.select('.x-paging-info', true).first();
20864 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20865 //            this.displayEl = navel.el.select('span',true).first();
20866         }
20867         
20868         var _this = this;
20869         
20870         if(this.buttons){
20871             Roo.each(_this.buttons, function(e){
20872                Roo.factory(e).onRender(_this.el, null);
20873             });
20874         }
20875             
20876         Roo.each(_this.toolbarItems, function(e) {
20877             _this.navgroup.addItem(e);
20878         });
20879         
20880         
20881         this.first = this.navgroup.addItem({
20882             tooltip: this.firstText,
20883             cls: "prev",
20884             icon : 'fa fa-backward',
20885             disabled: true,
20886             preventDefault: true,
20887             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20888         });
20889         
20890         this.prev =  this.navgroup.addItem({
20891             tooltip: this.prevText,
20892             cls: "prev",
20893             icon : 'fa fa-step-backward',
20894             disabled: true,
20895             preventDefault: true,
20896             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20897         });
20898     //this.addSeparator();
20899         
20900         
20901         var field = this.navgroup.addItem( {
20902             tagtype : 'span',
20903             cls : 'x-paging-position',
20904             
20905             html : this.beforePageText  +
20906                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20907                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20908          } ); //?? escaped?
20909         
20910         this.field = field.el.select('input', true).first();
20911         this.field.on("keydown", this.onPagingKeydown, this);
20912         this.field.on("focus", function(){this.dom.select();});
20913     
20914     
20915         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20916         //this.field.setHeight(18);
20917         //this.addSeparator();
20918         this.next = this.navgroup.addItem({
20919             tooltip: this.nextText,
20920             cls: "next",
20921             html : ' <i class="fa fa-step-forward">',
20922             disabled: true,
20923             preventDefault: true,
20924             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20925         });
20926         this.last = this.navgroup.addItem({
20927             tooltip: this.lastText,
20928             icon : 'fa fa-forward',
20929             cls: "next",
20930             disabled: true,
20931             preventDefault: true,
20932             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20933         });
20934     //this.addSeparator();
20935         this.loading = this.navgroup.addItem({
20936             tooltip: this.refreshText,
20937             icon: 'fa fa-refresh',
20938             preventDefault: true,
20939             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20940         });
20941
20942     },
20943
20944     // private
20945     updateInfo : function(){
20946         if(this.displayEl){
20947             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20948             var msg = count == 0 ?
20949                 this.emptyMsg :
20950                 String.format(
20951                     this.displayMsg,
20952                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20953                 );
20954             this.displayEl.update(msg);
20955         }
20956     },
20957
20958     // private
20959     onLoad : function(ds, r, o){
20960        this.cursor = o.params ? o.params.start : 0;
20961        var d = this.getPageData(),
20962             ap = d.activePage,
20963             ps = d.pages;
20964         
20965        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20966        this.field.dom.value = ap;
20967        this.first.setDisabled(ap == 1);
20968        this.prev.setDisabled(ap == 1);
20969        this.next.setDisabled(ap == ps);
20970        this.last.setDisabled(ap == ps);
20971        this.loading.enable();
20972        this.updateInfo();
20973     },
20974
20975     // private
20976     getPageData : function(){
20977         var total = this.ds.getTotalCount();
20978         return {
20979             total : total,
20980             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20981             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20982         };
20983     },
20984
20985     // private
20986     onLoadError : function(){
20987         this.loading.enable();
20988     },
20989
20990     // private
20991     onPagingKeydown : function(e){
20992         var k = e.getKey();
20993         var d = this.getPageData();
20994         if(k == e.RETURN){
20995             var v = this.field.dom.value, pageNum;
20996             if(!v || isNaN(pageNum = parseInt(v, 10))){
20997                 this.field.dom.value = d.activePage;
20998                 return;
20999             }
21000             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21001             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21002             e.stopEvent();
21003         }
21004         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))
21005         {
21006           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21007           this.field.dom.value = pageNum;
21008           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21009           e.stopEvent();
21010         }
21011         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21012         {
21013           var v = this.field.dom.value, pageNum; 
21014           var increment = (e.shiftKey) ? 10 : 1;
21015           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21016             increment *= -1;
21017           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21018             this.field.dom.value = d.activePage;
21019             return;
21020           }
21021           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21022           {
21023             this.field.dom.value = parseInt(v, 10) + increment;
21024             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21025             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21026           }
21027           e.stopEvent();
21028         }
21029     },
21030
21031     // private
21032     beforeLoad : function(){
21033         if(this.loading){
21034             this.loading.disable();
21035         }
21036     },
21037
21038     // private
21039     onClick : function(which){
21040         
21041         var ds = this.ds;
21042         if (!ds) {
21043             return;
21044         }
21045         
21046         switch(which){
21047             case "first":
21048                 ds.load({params:{start: 0, limit: this.pageSize}});
21049             break;
21050             case "prev":
21051                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21052             break;
21053             case "next":
21054                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21055             break;
21056             case "last":
21057                 var total = ds.getTotalCount();
21058                 var extra = total % this.pageSize;
21059                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21060                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21061             break;
21062             case "refresh":
21063                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21064             break;
21065         }
21066     },
21067
21068     /**
21069      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21070      * @param {Roo.data.Store} store The data store to unbind
21071      */
21072     unbind : function(ds){
21073         ds.un("beforeload", this.beforeLoad, this);
21074         ds.un("load", this.onLoad, this);
21075         ds.un("loadexception", this.onLoadError, this);
21076         ds.un("remove", this.updateInfo, this);
21077         ds.un("add", this.updateInfo, this);
21078         this.ds = undefined;
21079     },
21080
21081     /**
21082      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21083      * @param {Roo.data.Store} store The data store to bind
21084      */
21085     bind : function(ds){
21086         ds.on("beforeload", this.beforeLoad, this);
21087         ds.on("load", this.onLoad, this);
21088         ds.on("loadexception", this.onLoadError, this);
21089         ds.on("remove", this.updateInfo, this);
21090         ds.on("add", this.updateInfo, this);
21091         this.ds = ds;
21092     }
21093 });/*
21094  * - LGPL
21095  *
21096  * element
21097  * 
21098  */
21099
21100 /**
21101  * @class Roo.bootstrap.MessageBar
21102  * @extends Roo.bootstrap.Component
21103  * Bootstrap MessageBar class
21104  * @cfg {String} html contents of the MessageBar
21105  * @cfg {String} weight (info | success | warning | danger) default info
21106  * @cfg {String} beforeClass insert the bar before the given class
21107  * @cfg {Boolean} closable (true | false) default false
21108  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21109  * 
21110  * @constructor
21111  * Create a new Element
21112  * @param {Object} config The config object
21113  */
21114
21115 Roo.bootstrap.MessageBar = function(config){
21116     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21117 };
21118
21119 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21120     
21121     html: '',
21122     weight: 'info',
21123     closable: false,
21124     fixed: false,
21125     beforeClass: 'bootstrap-sticky-wrap',
21126     
21127     getAutoCreate : function(){
21128         
21129         var cfg = {
21130             tag: 'div',
21131             cls: 'alert alert-dismissable alert-' + this.weight,
21132             cn: [
21133                 {
21134                     tag: 'span',
21135                     cls: 'message',
21136                     html: this.html || ''
21137                 }
21138             ]
21139         }
21140         
21141         if(this.fixed){
21142             cfg.cls += ' alert-messages-fixed';
21143         }
21144         
21145         if(this.closable){
21146             cfg.cn.push({
21147                 tag: 'button',
21148                 cls: 'close',
21149                 html: 'x'
21150             });
21151         }
21152         
21153         return cfg;
21154     },
21155     
21156     onRender : function(ct, position)
21157     {
21158         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21159         
21160         if(!this.el){
21161             var cfg = Roo.apply({},  this.getAutoCreate());
21162             cfg.id = Roo.id();
21163             
21164             if (this.cls) {
21165                 cfg.cls += ' ' + this.cls;
21166             }
21167             if (this.style) {
21168                 cfg.style = this.style;
21169             }
21170             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21171             
21172             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21173         }
21174         
21175         this.el.select('>button.close').on('click', this.hide, this);
21176         
21177     },
21178     
21179     show : function()
21180     {
21181         if (!this.rendered) {
21182             this.render();
21183         }
21184         
21185         this.el.show();
21186         
21187         this.fireEvent('show', this);
21188         
21189     },
21190     
21191     hide : function()
21192     {
21193         if (!this.rendered) {
21194             this.render();
21195         }
21196         
21197         this.el.hide();
21198         
21199         this.fireEvent('hide', this);
21200     },
21201     
21202     update : function()
21203     {
21204 //        var e = this.el.dom.firstChild;
21205 //        
21206 //        if(this.closable){
21207 //            e = e.nextSibling;
21208 //        }
21209 //        
21210 //        e.data = this.html || '';
21211
21212         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21213     }
21214    
21215 });
21216
21217  
21218
21219      /*
21220  * - LGPL
21221  *
21222  * Graph
21223  * 
21224  */
21225
21226
21227 /**
21228  * @class Roo.bootstrap.Graph
21229  * @extends Roo.bootstrap.Component
21230  * Bootstrap Graph class
21231 > Prameters
21232  -sm {number} sm 4
21233  -md {number} md 5
21234  @cfg {String} graphtype  bar | vbar | pie
21235  @cfg {number} g_x coodinator | centre x (pie)
21236  @cfg {number} g_y coodinator | centre y (pie)
21237  @cfg {number} g_r radius (pie)
21238  @cfg {number} g_height height of the chart (respected by all elements in the set)
21239  @cfg {number} g_width width of the chart (respected by all elements in the set)
21240  @cfg {Object} title The title of the chart
21241     
21242  -{Array}  values
21243  -opts (object) options for the chart 
21244      o {
21245      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21246      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21247      o vgutter (number)
21248      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.
21249      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21250      o to
21251      o stretch (boolean)
21252      o }
21253  -opts (object) options for the pie
21254      o{
21255      o cut
21256      o startAngle (number)
21257      o endAngle (number)
21258      } 
21259  *
21260  * @constructor
21261  * Create a new Input
21262  * @param {Object} config The config object
21263  */
21264
21265 Roo.bootstrap.Graph = function(config){
21266     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21267     
21268     this.addEvents({
21269         // img events
21270         /**
21271          * @event click
21272          * The img click event for the img.
21273          * @param {Roo.EventObject} e
21274          */
21275         "click" : true
21276     });
21277 };
21278
21279 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21280     
21281     sm: 4,
21282     md: 5,
21283     graphtype: 'bar',
21284     g_height: 250,
21285     g_width: 400,
21286     g_x: 50,
21287     g_y: 50,
21288     g_r: 30,
21289     opts:{
21290         //g_colors: this.colors,
21291         g_type: 'soft',
21292         g_gutter: '20%'
21293
21294     },
21295     title : false,
21296
21297     getAutoCreate : function(){
21298         
21299         var cfg = {
21300             tag: 'div',
21301             html : null
21302         }
21303         
21304         
21305         return  cfg;
21306     },
21307
21308     onRender : function(ct,position){
21309         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21310         this.raphael = Raphael(this.el.dom);
21311         
21312                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21313                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21314                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21315                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21316                 /*
21317                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21318                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21319                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21320                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21321                 
21322                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21323                 r.barchart(330, 10, 300, 220, data1);
21324                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21325                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21326                 */
21327                 
21328                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21329                 // r.barchart(30, 30, 560, 250,  xdata, {
21330                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21331                 //     axis : "0 0 1 1",
21332                 //     axisxlabels :  xdata
21333                 //     //yvalues : cols,
21334                    
21335                 // });
21336 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21337 //        
21338 //        this.load(null,xdata,{
21339 //                axis : "0 0 1 1",
21340 //                axisxlabels :  xdata
21341 //                });
21342
21343     },
21344
21345     load : function(graphtype,xdata,opts){
21346         this.raphael.clear();
21347         if(!graphtype) {
21348             graphtype = this.graphtype;
21349         }
21350         if(!opts){
21351             opts = this.opts;
21352         }
21353         var r = this.raphael,
21354             fin = function () {
21355                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21356             },
21357             fout = function () {
21358                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21359             },
21360             pfin = function() {
21361                 this.sector.stop();
21362                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21363
21364                 if (this.label) {
21365                     this.label[0].stop();
21366                     this.label[0].attr({ r: 7.5 });
21367                     this.label[1].attr({ "font-weight": 800 });
21368                 }
21369             },
21370             pfout = function() {
21371                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21372
21373                 if (this.label) {
21374                     this.label[0].animate({ r: 5 }, 500, "bounce");
21375                     this.label[1].attr({ "font-weight": 400 });
21376                 }
21377             };
21378
21379         switch(graphtype){
21380             case 'bar':
21381                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21382                 break;
21383             case 'hbar':
21384                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21385                 break;
21386             case 'pie':
21387 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21388 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21389 //            
21390                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21391                 
21392                 break;
21393
21394         }
21395         
21396         if(this.title){
21397             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21398         }
21399         
21400     },
21401     
21402     setTitle: function(o)
21403     {
21404         this.title = o;
21405     },
21406     
21407     initEvents: function() {
21408         
21409         if(!this.href){
21410             this.el.on('click', this.onClick, this);
21411         }
21412     },
21413     
21414     onClick : function(e)
21415     {
21416         Roo.log('img onclick');
21417         this.fireEvent('click', this, e);
21418     }
21419    
21420 });
21421
21422  
21423 /*
21424  * - LGPL
21425  *
21426  * numberBox
21427  * 
21428  */
21429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21430
21431 /**
21432  * @class Roo.bootstrap.dash.NumberBox
21433  * @extends Roo.bootstrap.Component
21434  * Bootstrap NumberBox class
21435  * @cfg {String} headline Box headline
21436  * @cfg {String} content Box content
21437  * @cfg {String} icon Box icon
21438  * @cfg {String} footer Footer text
21439  * @cfg {String} fhref Footer href
21440  * 
21441  * @constructor
21442  * Create a new NumberBox
21443  * @param {Object} config The config object
21444  */
21445
21446
21447 Roo.bootstrap.dash.NumberBox = function(config){
21448     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21449     
21450 };
21451
21452 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21453     
21454     headline : '',
21455     content : '',
21456     icon : '',
21457     footer : '',
21458     fhref : '',
21459     ficon : '',
21460     
21461     getAutoCreate : function(){
21462         
21463         var cfg = {
21464             tag : 'div',
21465             cls : 'small-box ',
21466             cn : [
21467                 {
21468                     tag : 'div',
21469                     cls : 'inner',
21470                     cn :[
21471                         {
21472                             tag : 'h3',
21473                             cls : 'roo-headline',
21474                             html : this.headline
21475                         },
21476                         {
21477                             tag : 'p',
21478                             cls : 'roo-content',
21479                             html : this.content
21480                         }
21481                     ]
21482                 }
21483             ]
21484         }
21485         
21486         if(this.icon){
21487             cfg.cn.push({
21488                 tag : 'div',
21489                 cls : 'icon',
21490                 cn :[
21491                     {
21492                         tag : 'i',
21493                         cls : 'ion ' + this.icon
21494                     }
21495                 ]
21496             });
21497         }
21498         
21499         if(this.footer){
21500             var footer = {
21501                 tag : 'a',
21502                 cls : 'small-box-footer',
21503                 href : this.fhref || '#',
21504                 html : this.footer
21505             };
21506             
21507             cfg.cn.push(footer);
21508             
21509         }
21510         
21511         return  cfg;
21512     },
21513
21514     onRender : function(ct,position){
21515         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21516
21517
21518        
21519                 
21520     },
21521
21522     setHeadline: function (value)
21523     {
21524         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21525     },
21526     
21527     setFooter: function (value, href)
21528     {
21529         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21530         
21531         if(href){
21532             this.el.select('a.small-box-footer',true).first().attr('href', href);
21533         }
21534         
21535     },
21536
21537     setContent: function (value)
21538     {
21539         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21540     },
21541
21542     initEvents: function() 
21543     {   
21544         
21545     }
21546     
21547 });
21548
21549  
21550 /*
21551  * - LGPL
21552  *
21553  * TabBox
21554  * 
21555  */
21556 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21557
21558 /**
21559  * @class Roo.bootstrap.dash.TabBox
21560  * @extends Roo.bootstrap.Component
21561  * Bootstrap TabBox class
21562  * @cfg {String} title Title of the TabBox
21563  * @cfg {String} icon Icon of the TabBox
21564  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21565  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21566  * 
21567  * @constructor
21568  * Create a new TabBox
21569  * @param {Object} config The config object
21570  */
21571
21572
21573 Roo.bootstrap.dash.TabBox = function(config){
21574     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21575     this.addEvents({
21576         // raw events
21577         /**
21578          * @event addpane
21579          * When a pane is added
21580          * @param {Roo.bootstrap.dash.TabPane} pane
21581          */
21582         "addpane" : true,
21583         /**
21584          * @event activatepane
21585          * When a pane is activated
21586          * @param {Roo.bootstrap.dash.TabPane} pane
21587          */
21588         "activatepane" : true
21589         
21590          
21591     });
21592     
21593     this.panes = [];
21594 };
21595
21596 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21597
21598     title : '',
21599     icon : false,
21600     showtabs : true,
21601     tabScrollable : false,
21602     
21603     getChildContainer : function()
21604     {
21605         return this.el.select('.tab-content', true).first();
21606     },
21607     
21608     getAutoCreate : function(){
21609         
21610         var header = {
21611             tag: 'li',
21612             cls: 'pull-left header',
21613             html: this.title,
21614             cn : []
21615         };
21616         
21617         if(this.icon){
21618             header.cn.push({
21619                 tag: 'i',
21620                 cls: 'fa ' + this.icon
21621             });
21622         }
21623         
21624         var h = {
21625             tag: 'ul',
21626             cls: 'nav nav-tabs pull-right',
21627             cn: [
21628                 header
21629             ]
21630         };
21631         
21632         if(this.tabScrollable){
21633             h = {
21634                 tag: 'div',
21635                 cls: 'tab-header',
21636                 cn: [
21637                     {
21638                         tag: 'ul',
21639                         cls: 'nav nav-tabs pull-right',
21640                         cn: [
21641                             header
21642                         ]
21643                     }
21644                 ]
21645             }
21646         }
21647         
21648         var cfg = {
21649             tag: 'div',
21650             cls: 'nav-tabs-custom',
21651             cn: [
21652                 h,
21653                 {
21654                     tag: 'div',
21655                     cls: 'tab-content no-padding',
21656                     cn: []
21657                 }
21658             ]
21659         }
21660
21661         return  cfg;
21662     },
21663     initEvents : function()
21664     {
21665         //Roo.log('add add pane handler');
21666         this.on('addpane', this.onAddPane, this);
21667     },
21668      /**
21669      * Updates the box title
21670      * @param {String} html to set the title to.
21671      */
21672     setTitle : function(value)
21673     {
21674         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21675     },
21676     onAddPane : function(pane)
21677     {
21678         this.panes.push(pane);
21679         //Roo.log('addpane');
21680         //Roo.log(pane);
21681         // tabs are rendere left to right..
21682         if(!this.showtabs){
21683             return;
21684         }
21685         
21686         var ctr = this.el.select('.nav-tabs', true).first();
21687          
21688          
21689         var existing = ctr.select('.nav-tab',true);
21690         var qty = existing.getCount();;
21691         
21692         
21693         var tab = ctr.createChild({
21694             tag : 'li',
21695             cls : 'nav-tab' + (qty ? '' : ' active'),
21696             cn : [
21697                 {
21698                     tag : 'a',
21699                     href:'#',
21700                     html : pane.title
21701                 }
21702             ]
21703         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21704         pane.tab = tab;
21705         
21706         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21707         if (!qty) {
21708             pane.el.addClass('active');
21709         }
21710         
21711                 
21712     },
21713     onTabClick : function(ev,un,ob,pane)
21714     {
21715         //Roo.log('tab - prev default');
21716         ev.preventDefault();
21717         
21718         
21719         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21720         pane.tab.addClass('active');
21721         //Roo.log(pane.title);
21722         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21723         // technically we should have a deactivate event.. but maybe add later.
21724         // and it should not de-activate the selected tab...
21725         this.fireEvent('activatepane', pane);
21726         pane.el.addClass('active');
21727         pane.fireEvent('activate');
21728         
21729         
21730     },
21731     
21732     getActivePane : function()
21733     {
21734         var r = false;
21735         Roo.each(this.panes, function(p) {
21736             if(p.el.hasClass('active')){
21737                 r = p;
21738                 return false;
21739             }
21740             
21741             return;
21742         });
21743         
21744         return r;
21745     }
21746     
21747     
21748 });
21749
21750  
21751 /*
21752  * - LGPL
21753  *
21754  * Tab pane
21755  * 
21756  */
21757 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21758 /**
21759  * @class Roo.bootstrap.TabPane
21760  * @extends Roo.bootstrap.Component
21761  * Bootstrap TabPane class
21762  * @cfg {Boolean} active (false | true) Default false
21763  * @cfg {String} title title of panel
21764
21765  * 
21766  * @constructor
21767  * Create a new TabPane
21768  * @param {Object} config The config object
21769  */
21770
21771 Roo.bootstrap.dash.TabPane = function(config){
21772     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21773     
21774     this.addEvents({
21775         // raw events
21776         /**
21777          * @event activate
21778          * When a pane is activated
21779          * @param {Roo.bootstrap.dash.TabPane} pane
21780          */
21781         "activate" : true
21782          
21783     });
21784 };
21785
21786 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21787     
21788     active : false,
21789     title : '',
21790     
21791     // the tabBox that this is attached to.
21792     tab : false,
21793      
21794     getAutoCreate : function() 
21795     {
21796         var cfg = {
21797             tag: 'div',
21798             cls: 'tab-pane'
21799         }
21800         
21801         if(this.active){
21802             cfg.cls += ' active';
21803         }
21804         
21805         return cfg;
21806     },
21807     initEvents  : function()
21808     {
21809         //Roo.log('trigger add pane handler');
21810         this.parent().fireEvent('addpane', this)
21811     },
21812     
21813      /**
21814      * Updates the tab title 
21815      * @param {String} html to set the title to.
21816      */
21817     setTitle: function(str)
21818     {
21819         if (!this.tab) {
21820             return;
21821         }
21822         this.title = str;
21823         this.tab.select('a', true).first().dom.innerHTML = str;
21824         
21825     }
21826     
21827     
21828     
21829 });
21830
21831  
21832
21833
21834  /*
21835  * - LGPL
21836  *
21837  * menu
21838  * 
21839  */
21840 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21841
21842 /**
21843  * @class Roo.bootstrap.menu.Menu
21844  * @extends Roo.bootstrap.Component
21845  * Bootstrap Menu class - container for Menu
21846  * @cfg {String} html Text of the menu
21847  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21848  * @cfg {String} icon Font awesome icon
21849  * @cfg {String} pos Menu align to (top | bottom) default bottom
21850  * 
21851  * 
21852  * @constructor
21853  * Create a new Menu
21854  * @param {Object} config The config object
21855  */
21856
21857
21858 Roo.bootstrap.menu.Menu = function(config){
21859     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21860     
21861     this.addEvents({
21862         /**
21863          * @event beforeshow
21864          * Fires before this menu is displayed
21865          * @param {Roo.bootstrap.menu.Menu} this
21866          */
21867         beforeshow : true,
21868         /**
21869          * @event beforehide
21870          * Fires before this menu is hidden
21871          * @param {Roo.bootstrap.menu.Menu} this
21872          */
21873         beforehide : true,
21874         /**
21875          * @event show
21876          * Fires after this menu is displayed
21877          * @param {Roo.bootstrap.menu.Menu} this
21878          */
21879         show : true,
21880         /**
21881          * @event hide
21882          * Fires after this menu is hidden
21883          * @param {Roo.bootstrap.menu.Menu} this
21884          */
21885         hide : true,
21886         /**
21887          * @event click
21888          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21889          * @param {Roo.bootstrap.menu.Menu} this
21890          * @param {Roo.EventObject} e
21891          */
21892         click : true
21893     });
21894     
21895 };
21896
21897 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21898     
21899     submenu : false,
21900     html : '',
21901     weight : 'default',
21902     icon : false,
21903     pos : 'bottom',
21904     
21905     
21906     getChildContainer : function() {
21907         if(this.isSubMenu){
21908             return this.el;
21909         }
21910         
21911         return this.el.select('ul.dropdown-menu', true).first();  
21912     },
21913     
21914     getAutoCreate : function()
21915     {
21916         var text = [
21917             {
21918                 tag : 'span',
21919                 cls : 'roo-menu-text',
21920                 html : this.html
21921             }
21922         ];
21923         
21924         if(this.icon){
21925             text.unshift({
21926                 tag : 'i',
21927                 cls : 'fa ' + this.icon
21928             })
21929         }
21930         
21931         
21932         var cfg = {
21933             tag : 'div',
21934             cls : 'btn-group',
21935             cn : [
21936                 {
21937                     tag : 'button',
21938                     cls : 'dropdown-button btn btn-' + this.weight,
21939                     cn : text
21940                 },
21941                 {
21942                     tag : 'button',
21943                     cls : 'dropdown-toggle btn btn-' + this.weight,
21944                     cn : [
21945                         {
21946                             tag : 'span',
21947                             cls : 'caret'
21948                         }
21949                     ]
21950                 },
21951                 {
21952                     tag : 'ul',
21953                     cls : 'dropdown-menu'
21954                 }
21955             ]
21956             
21957         };
21958         
21959         if(this.pos == 'top'){
21960             cfg.cls += ' dropup';
21961         }
21962         
21963         if(this.isSubMenu){
21964             cfg = {
21965                 tag : 'ul',
21966                 cls : 'dropdown-menu'
21967             }
21968         }
21969         
21970         return cfg;
21971     },
21972     
21973     onRender : function(ct, position)
21974     {
21975         this.isSubMenu = ct.hasClass('dropdown-submenu');
21976         
21977         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21978     },
21979     
21980     initEvents : function() 
21981     {
21982         if(this.isSubMenu){
21983             return;
21984         }
21985         
21986         this.hidden = true;
21987         
21988         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21989         this.triggerEl.on('click', this.onTriggerPress, this);
21990         
21991         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21992         this.buttonEl.on('click', this.onClick, this);
21993         
21994     },
21995     
21996     list : function()
21997     {
21998         if(this.isSubMenu){
21999             return this.el;
22000         }
22001         
22002         return this.el.select('ul.dropdown-menu', true).first();
22003     },
22004     
22005     onClick : function(e)
22006     {
22007         this.fireEvent("click", this, e);
22008     },
22009     
22010     onTriggerPress  : function(e)
22011     {   
22012         if (this.isVisible()) {
22013             this.hide();
22014         } else {
22015             this.show();
22016         }
22017     },
22018     
22019     isVisible : function(){
22020         return !this.hidden;
22021     },
22022     
22023     show : function()
22024     {
22025         this.fireEvent("beforeshow", this);
22026         
22027         this.hidden = false;
22028         this.el.addClass('open');
22029         
22030         Roo.get(document).on("mouseup", this.onMouseUp, this);
22031         
22032         this.fireEvent("show", this);
22033         
22034         
22035     },
22036     
22037     hide : function()
22038     {
22039         this.fireEvent("beforehide", this);
22040         
22041         this.hidden = true;
22042         this.el.removeClass('open');
22043         
22044         Roo.get(document).un("mouseup", this.onMouseUp);
22045         
22046         this.fireEvent("hide", this);
22047     },
22048     
22049     onMouseUp : function()
22050     {
22051         this.hide();
22052     }
22053     
22054 });
22055
22056  
22057  /*
22058  * - LGPL
22059  *
22060  * menu item
22061  * 
22062  */
22063 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22064
22065 /**
22066  * @class Roo.bootstrap.menu.Item
22067  * @extends Roo.bootstrap.Component
22068  * Bootstrap MenuItem class
22069  * @cfg {Boolean} submenu (true | false) default false
22070  * @cfg {String} html text of the item
22071  * @cfg {String} href the link
22072  * @cfg {Boolean} disable (true | false) default false
22073  * @cfg {Boolean} preventDefault (true | false) default true
22074  * @cfg {String} icon Font awesome icon
22075  * @cfg {String} pos Submenu align to (left | right) default right 
22076  * 
22077  * 
22078  * @constructor
22079  * Create a new Item
22080  * @param {Object} config The config object
22081  */
22082
22083
22084 Roo.bootstrap.menu.Item = function(config){
22085     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22086     this.addEvents({
22087         /**
22088          * @event mouseover
22089          * Fires when the mouse is hovering over this menu
22090          * @param {Roo.bootstrap.menu.Item} this
22091          * @param {Roo.EventObject} e
22092          */
22093         mouseover : true,
22094         /**
22095          * @event mouseout
22096          * Fires when the mouse exits this menu
22097          * @param {Roo.bootstrap.menu.Item} this
22098          * @param {Roo.EventObject} e
22099          */
22100         mouseout : true,
22101         // raw events
22102         /**
22103          * @event click
22104          * The raw click event for the entire grid.
22105          * @param {Roo.EventObject} e
22106          */
22107         click : true
22108     });
22109 };
22110
22111 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22112     
22113     submenu : false,
22114     href : '',
22115     html : '',
22116     preventDefault: true,
22117     disable : false,
22118     icon : false,
22119     pos : 'right',
22120     
22121     getAutoCreate : function()
22122     {
22123         var text = [
22124             {
22125                 tag : 'span',
22126                 cls : 'roo-menu-item-text',
22127                 html : this.html
22128             }
22129         ];
22130         
22131         if(this.icon){
22132             text.unshift({
22133                 tag : 'i',
22134                 cls : 'fa ' + this.icon
22135             })
22136         }
22137         
22138         var cfg = {
22139             tag : 'li',
22140             cn : [
22141                 {
22142                     tag : 'a',
22143                     href : this.href || '#',
22144                     cn : text
22145                 }
22146             ]
22147         };
22148         
22149         if(this.disable){
22150             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22151         }
22152         
22153         if(this.submenu){
22154             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22155             
22156             if(this.pos == 'left'){
22157                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22158             }
22159         }
22160         
22161         return cfg;
22162     },
22163     
22164     initEvents : function() 
22165     {
22166         this.el.on('mouseover', this.onMouseOver, this);
22167         this.el.on('mouseout', this.onMouseOut, this);
22168         
22169         this.el.select('a', true).first().on('click', this.onClick, this);
22170         
22171     },
22172     
22173     onClick : function(e)
22174     {
22175         if(this.preventDefault){
22176             e.preventDefault();
22177         }
22178         
22179         this.fireEvent("click", this, e);
22180     },
22181     
22182     onMouseOver : function(e)
22183     {
22184         if(this.submenu && this.pos == 'left'){
22185             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22186         }
22187         
22188         this.fireEvent("mouseover", this, e);
22189     },
22190     
22191     onMouseOut : function(e)
22192     {
22193         this.fireEvent("mouseout", this, e);
22194     }
22195 });
22196
22197  
22198
22199  /*
22200  * - LGPL
22201  *
22202  * menu separator
22203  * 
22204  */
22205 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22206
22207 /**
22208  * @class Roo.bootstrap.menu.Separator
22209  * @extends Roo.bootstrap.Component
22210  * Bootstrap Separator class
22211  * 
22212  * @constructor
22213  * Create a new Separator
22214  * @param {Object} config The config object
22215  */
22216
22217
22218 Roo.bootstrap.menu.Separator = function(config){
22219     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22220 };
22221
22222 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22223     
22224     getAutoCreate : function(){
22225         var cfg = {
22226             tag : 'li',
22227             cls: 'divider'
22228         };
22229         
22230         return cfg;
22231     }
22232    
22233 });
22234
22235  
22236
22237  /*
22238  * - LGPL
22239  *
22240  * Tooltip
22241  * 
22242  */
22243
22244 /**
22245  * @class Roo.bootstrap.Tooltip
22246  * Bootstrap Tooltip class
22247  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22248  * to determine which dom element triggers the tooltip.
22249  * 
22250  * It needs to add support for additional attributes like tooltip-position
22251  * 
22252  * @constructor
22253  * Create a new Toolti
22254  * @param {Object} config The config object
22255  */
22256
22257 Roo.bootstrap.Tooltip = function(config){
22258     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22259 };
22260
22261 Roo.apply(Roo.bootstrap.Tooltip, {
22262     /**
22263      * @function init initialize tooltip monitoring.
22264      * @static
22265      */
22266     currentEl : false,
22267     currentTip : false,
22268     currentRegion : false,
22269     
22270     //  init : delay?
22271     
22272     init : function()
22273     {
22274         Roo.get(document).on('mouseover', this.enter ,this);
22275         Roo.get(document).on('mouseout', this.leave, this);
22276          
22277         
22278         this.currentTip = new Roo.bootstrap.Tooltip();
22279     },
22280     
22281     enter : function(ev)
22282     {
22283         var dom = ev.getTarget();
22284         
22285         //Roo.log(['enter',dom]);
22286         var el = Roo.fly(dom);
22287         if (this.currentEl) {
22288             //Roo.log(dom);
22289             //Roo.log(this.currentEl);
22290             //Roo.log(this.currentEl.contains(dom));
22291             if (this.currentEl == el) {
22292                 return;
22293             }
22294             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22295                 return;
22296             }
22297
22298         }
22299         
22300         
22301         
22302         if (this.currentTip.el) {
22303             this.currentTip.el.hide(); // force hiding...
22304         }    
22305         //Roo.log(ev);
22306         var bindEl = el;
22307         
22308         // you can not look for children, as if el is the body.. then everythign is the child..
22309         if (!el.attr('tooltip')) { //
22310             if (!el.select("[tooltip]").elements.length) {
22311                 return;
22312             }
22313             // is the mouse over this child...?
22314             bindEl = el.select("[tooltip]").first();
22315             var xy = ev.getXY();
22316             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22317                 //Roo.log("not in region.");
22318                 return;
22319             }
22320             //Roo.log("child element over..");
22321             
22322         }
22323         this.currentEl = bindEl;
22324         this.currentTip.bind(bindEl);
22325         this.currentRegion = Roo.lib.Region.getRegion(dom);
22326         this.currentTip.enter();
22327         
22328     },
22329     leave : function(ev)
22330     {
22331         var dom = ev.getTarget();
22332         //Roo.log(['leave',dom]);
22333         if (!this.currentEl) {
22334             return;
22335         }
22336         
22337         
22338         if (dom != this.currentEl.dom) {
22339             return;
22340         }
22341         var xy = ev.getXY();
22342         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22343             return;
22344         }
22345         // only activate leave if mouse cursor is outside... bounding box..
22346         
22347         
22348         
22349         
22350         if (this.currentTip) {
22351             this.currentTip.leave();
22352         }
22353         //Roo.log('clear currentEl');
22354         this.currentEl = false;
22355         
22356         
22357     },
22358     alignment : {
22359         'left' : ['r-l', [-2,0], 'right'],
22360         'right' : ['l-r', [2,0], 'left'],
22361         'bottom' : ['t-b', [0,2], 'top'],
22362         'top' : [ 'b-t', [0,-2], 'bottom']
22363     }
22364     
22365 });
22366
22367
22368 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22369     
22370     
22371     bindEl : false,
22372     
22373     delay : null, // can be { show : 300 , hide: 500}
22374     
22375     timeout : null,
22376     
22377     hoverState : null, //???
22378     
22379     placement : 'bottom', 
22380     
22381     getAutoCreate : function(){
22382     
22383         var cfg = {
22384            cls : 'tooltip',
22385            role : 'tooltip',
22386            cn : [
22387                 {
22388                     cls : 'tooltip-arrow'
22389                 },
22390                 {
22391                     cls : 'tooltip-inner'
22392                 }
22393            ]
22394         };
22395         
22396         return cfg;
22397     },
22398     bind : function(el)
22399     {
22400         this.bindEl = el;
22401     },
22402       
22403     
22404     enter : function () {
22405        
22406         if (this.timeout != null) {
22407             clearTimeout(this.timeout);
22408         }
22409         
22410         this.hoverState = 'in';
22411          //Roo.log("enter - show");
22412         if (!this.delay || !this.delay.show) {
22413             this.show();
22414             return;
22415         }
22416         var _t = this;
22417         this.timeout = setTimeout(function () {
22418             if (_t.hoverState == 'in') {
22419                 _t.show();
22420             }
22421         }, this.delay.show);
22422     },
22423     leave : function()
22424     {
22425         clearTimeout(this.timeout);
22426     
22427         this.hoverState = 'out';
22428          if (!this.delay || !this.delay.hide) {
22429             this.hide();
22430             return;
22431         }
22432        
22433         var _t = this;
22434         this.timeout = setTimeout(function () {
22435             //Roo.log("leave - timeout");
22436             
22437             if (_t.hoverState == 'out') {
22438                 _t.hide();
22439                 Roo.bootstrap.Tooltip.currentEl = false;
22440             }
22441         }, delay);
22442     },
22443     
22444     show : function ()
22445     {
22446         if (!this.el) {
22447             this.render(document.body);
22448         }
22449         // set content.
22450         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22451         
22452         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22453         
22454         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22455         
22456         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22457         
22458         var placement = typeof this.placement == 'function' ?
22459             this.placement.call(this, this.el, on_el) :
22460             this.placement;
22461             
22462         var autoToken = /\s?auto?\s?/i;
22463         var autoPlace = autoToken.test(placement);
22464         if (autoPlace) {
22465             placement = placement.replace(autoToken, '') || 'top';
22466         }
22467         
22468         //this.el.detach()
22469         //this.el.setXY([0,0]);
22470         this.el.show();
22471         //this.el.dom.style.display='block';
22472         this.el.addClass(placement);
22473         
22474         //this.el.appendTo(on_el);
22475         
22476         var p = this.getPosition();
22477         var box = this.el.getBox();
22478         
22479         if (autoPlace) {
22480             // fixme..
22481         }
22482         var align = Roo.bootstrap.Tooltip.alignment[placement];
22483         this.el.alignTo(this.bindEl, align[0],align[1]);
22484         //var arrow = this.el.select('.arrow',true).first();
22485         //arrow.set(align[2], 
22486         
22487         this.el.addClass('in fade');
22488         this.hoverState = null;
22489         
22490         if (this.el.hasClass('fade')) {
22491             // fade it?
22492         }
22493         
22494     },
22495     hide : function()
22496     {
22497          
22498         if (!this.el) {
22499             return;
22500         }
22501         //this.el.setXY([0,0]);
22502         this.el.removeClass('in');
22503         //this.el.hide();
22504         
22505     }
22506     
22507 });
22508  
22509
22510  /*
22511  * - LGPL
22512  *
22513  * Location Picker
22514  * 
22515  */
22516
22517 /**
22518  * @class Roo.bootstrap.LocationPicker
22519  * @extends Roo.bootstrap.Component
22520  * Bootstrap LocationPicker class
22521  * @cfg {Number} latitude Position when init default 0
22522  * @cfg {Number} longitude Position when init default 0
22523  * @cfg {Number} zoom default 15
22524  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22525  * @cfg {Boolean} mapTypeControl default false
22526  * @cfg {Boolean} disableDoubleClickZoom default false
22527  * @cfg {Boolean} scrollwheel default true
22528  * @cfg {Boolean} streetViewControl default false
22529  * @cfg {Number} radius default 0
22530  * @cfg {String} locationName
22531  * @cfg {Boolean} draggable default true
22532  * @cfg {Boolean} enableAutocomplete default false
22533  * @cfg {Boolean} enableReverseGeocode default true
22534  * @cfg {String} markerTitle
22535  * 
22536  * @constructor
22537  * Create a new LocationPicker
22538  * @param {Object} config The config object
22539  */
22540
22541
22542 Roo.bootstrap.LocationPicker = function(config){
22543     
22544     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22545     
22546     this.addEvents({
22547         /**
22548          * @event initial
22549          * Fires when the picker initialized.
22550          * @param {Roo.bootstrap.LocationPicker} this
22551          * @param {Google Location} location
22552          */
22553         initial : true,
22554         /**
22555          * @event positionchanged
22556          * Fires when the picker position changed.
22557          * @param {Roo.bootstrap.LocationPicker} this
22558          * @param {Google Location} location
22559          */
22560         positionchanged : true,
22561         /**
22562          * @event resize
22563          * Fires when the map resize.
22564          * @param {Roo.bootstrap.LocationPicker} this
22565          */
22566         resize : true,
22567         /**
22568          * @event show
22569          * Fires when the map show.
22570          * @param {Roo.bootstrap.LocationPicker} this
22571          */
22572         show : true,
22573         /**
22574          * @event hide
22575          * Fires when the map hide.
22576          * @param {Roo.bootstrap.LocationPicker} this
22577          */
22578         hide : true,
22579         /**
22580          * @event mapClick
22581          * Fires when click the map.
22582          * @param {Roo.bootstrap.LocationPicker} this
22583          * @param {Map event} e
22584          */
22585         mapClick : true,
22586         /**
22587          * @event mapRightClick
22588          * Fires when right click the map.
22589          * @param {Roo.bootstrap.LocationPicker} this
22590          * @param {Map event} e
22591          */
22592         mapRightClick : true,
22593         /**
22594          * @event markerClick
22595          * Fires when click the marker.
22596          * @param {Roo.bootstrap.LocationPicker} this
22597          * @param {Map event} e
22598          */
22599         markerClick : true,
22600         /**
22601          * @event markerRightClick
22602          * Fires when right click the marker.
22603          * @param {Roo.bootstrap.LocationPicker} this
22604          * @param {Map event} e
22605          */
22606         markerRightClick : true,
22607         /**
22608          * @event OverlayViewDraw
22609          * Fires when OverlayView Draw
22610          * @param {Roo.bootstrap.LocationPicker} this
22611          */
22612         OverlayViewDraw : true,
22613         /**
22614          * @event OverlayViewOnAdd
22615          * Fires when OverlayView Draw
22616          * @param {Roo.bootstrap.LocationPicker} this
22617          */
22618         OverlayViewOnAdd : true,
22619         /**
22620          * @event OverlayViewOnRemove
22621          * Fires when OverlayView Draw
22622          * @param {Roo.bootstrap.LocationPicker} this
22623          */
22624         OverlayViewOnRemove : true,
22625         /**
22626          * @event OverlayViewShow
22627          * Fires when OverlayView Draw
22628          * @param {Roo.bootstrap.LocationPicker} this
22629          * @param {Pixel} cpx
22630          */
22631         OverlayViewShow : true,
22632         /**
22633          * @event OverlayViewHide
22634          * Fires when OverlayView Draw
22635          * @param {Roo.bootstrap.LocationPicker} this
22636          */
22637         OverlayViewHide : true
22638     });
22639         
22640 };
22641
22642 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22643     
22644     gMapContext: false,
22645     
22646     latitude: 0,
22647     longitude: 0,
22648     zoom: 15,
22649     mapTypeId: false,
22650     mapTypeControl: false,
22651     disableDoubleClickZoom: false,
22652     scrollwheel: true,
22653     streetViewControl: false,
22654     radius: 0,
22655     locationName: '',
22656     draggable: true,
22657     enableAutocomplete: false,
22658     enableReverseGeocode: true,
22659     markerTitle: '',
22660     
22661     getAutoCreate: function()
22662     {
22663
22664         var cfg = {
22665             tag: 'div',
22666             cls: 'roo-location-picker'
22667         };
22668         
22669         return cfg
22670     },
22671     
22672     initEvents: function(ct, position)
22673     {       
22674         if(!this.el.getWidth() || this.isApplied()){
22675             return;
22676         }
22677         
22678         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22679         
22680         this.initial();
22681     },
22682     
22683     initial: function()
22684     {
22685         if(!this.mapTypeId){
22686             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22687         }
22688         
22689         this.gMapContext = this.GMapContext();
22690         
22691         this.initOverlayView();
22692         
22693         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22694         
22695         var _this = this;
22696                 
22697         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22698             _this.setPosition(_this.gMapContext.marker.position);
22699         });
22700         
22701         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22702             _this.fireEvent('mapClick', this, event);
22703             
22704         });
22705
22706         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22707             _this.fireEvent('mapRightClick', this, event);
22708             
22709         });
22710         
22711         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22712             _this.fireEvent('markerClick', this, event);
22713             
22714         });
22715
22716         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22717             _this.fireEvent('markerRightClick', this, event);
22718             
22719         });
22720         
22721         this.setPosition(this.gMapContext.location);
22722         
22723         this.fireEvent('initial', this, this.gMapContext.location);
22724     },
22725     
22726     initOverlayView: function()
22727     {
22728         var _this = this;
22729         
22730         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22731             
22732             draw: function()
22733             {
22734                 _this.fireEvent('OverlayViewDraw', _this);
22735             },
22736             
22737             onAdd: function()
22738             {
22739                 _this.fireEvent('OverlayViewOnAdd', _this);
22740             },
22741             
22742             onRemove: function()
22743             {
22744                 _this.fireEvent('OverlayViewOnRemove', _this);
22745             },
22746             
22747             show: function(cpx)
22748             {
22749                 _this.fireEvent('OverlayViewShow', _this, cpx);
22750             },
22751             
22752             hide: function()
22753             {
22754                 _this.fireEvent('OverlayViewHide', _this);
22755             }
22756             
22757         });
22758     },
22759     
22760     fromLatLngToContainerPixel: function(event)
22761     {
22762         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22763     },
22764     
22765     isApplied: function() 
22766     {
22767         return this.getGmapContext() == false ? false : true;
22768     },
22769     
22770     getGmapContext: function() 
22771     {
22772         return this.gMapContext
22773     },
22774     
22775     GMapContext: function() 
22776     {
22777         var position = new google.maps.LatLng(this.latitude, this.longitude);
22778         
22779         var _map = new google.maps.Map(this.el.dom, {
22780             center: position,
22781             zoom: this.zoom,
22782             mapTypeId: this.mapTypeId,
22783             mapTypeControl: this.mapTypeControl,
22784             disableDoubleClickZoom: this.disableDoubleClickZoom,
22785             scrollwheel: this.scrollwheel,
22786             streetViewControl: this.streetViewControl,
22787             locationName: this.locationName,
22788             draggable: this.draggable,
22789             enableAutocomplete: this.enableAutocomplete,
22790             enableReverseGeocode: this.enableReverseGeocode
22791         });
22792         
22793         var _marker = new google.maps.Marker({
22794             position: position,
22795             map: _map,
22796             title: this.markerTitle,
22797             draggable: this.draggable
22798         });
22799         
22800         return {
22801             map: _map,
22802             marker: _marker,
22803             circle: null,
22804             location: position,
22805             radius: this.radius,
22806             locationName: this.locationName,
22807             addressComponents: {
22808                 formatted_address: null,
22809                 addressLine1: null,
22810                 addressLine2: null,
22811                 streetName: null,
22812                 streetNumber: null,
22813                 city: null,
22814                 district: null,
22815                 state: null,
22816                 stateOrProvince: null
22817             },
22818             settings: this,
22819             domContainer: this.el.dom,
22820             geodecoder: new google.maps.Geocoder()
22821         };
22822     },
22823     
22824     drawCircle: function(center, radius, options) 
22825     {
22826         if (this.gMapContext.circle != null) {
22827             this.gMapContext.circle.setMap(null);
22828         }
22829         if (radius > 0) {
22830             radius *= 1;
22831             options = Roo.apply({}, options, {
22832                 strokeColor: "#0000FF",
22833                 strokeOpacity: .35,
22834                 strokeWeight: 2,
22835                 fillColor: "#0000FF",
22836                 fillOpacity: .2
22837             });
22838             
22839             options.map = this.gMapContext.map;
22840             options.radius = radius;
22841             options.center = center;
22842             this.gMapContext.circle = new google.maps.Circle(options);
22843             return this.gMapContext.circle;
22844         }
22845         
22846         return null;
22847     },
22848     
22849     setPosition: function(location) 
22850     {
22851         this.gMapContext.location = location;
22852         this.gMapContext.marker.setPosition(location);
22853         this.gMapContext.map.panTo(location);
22854         this.drawCircle(location, this.gMapContext.radius, {});
22855         
22856         var _this = this;
22857         
22858         if (this.gMapContext.settings.enableReverseGeocode) {
22859             this.gMapContext.geodecoder.geocode({
22860                 latLng: this.gMapContext.location
22861             }, function(results, status) {
22862                 
22863                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22864                     _this.gMapContext.locationName = results[0].formatted_address;
22865                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22866                     
22867                     _this.fireEvent('positionchanged', this, location);
22868                 }
22869             });
22870             
22871             return;
22872         }
22873         
22874         this.fireEvent('positionchanged', this, location);
22875     },
22876     
22877     resize: function()
22878     {
22879         google.maps.event.trigger(this.gMapContext.map, "resize");
22880         
22881         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22882         
22883         this.fireEvent('resize', this);
22884     },
22885     
22886     setPositionByLatLng: function(latitude, longitude)
22887     {
22888         this.setPosition(new google.maps.LatLng(latitude, longitude));
22889     },
22890     
22891     getCurrentPosition: function() 
22892     {
22893         return {
22894             latitude: this.gMapContext.location.lat(),
22895             longitude: this.gMapContext.location.lng()
22896         };
22897     },
22898     
22899     getAddressName: function() 
22900     {
22901         return this.gMapContext.locationName;
22902     },
22903     
22904     getAddressComponents: function() 
22905     {
22906         return this.gMapContext.addressComponents;
22907     },
22908     
22909     address_component_from_google_geocode: function(address_components) 
22910     {
22911         var result = {};
22912         
22913         for (var i = 0; i < address_components.length; i++) {
22914             var component = address_components[i];
22915             if (component.types.indexOf("postal_code") >= 0) {
22916                 result.postalCode = component.short_name;
22917             } else if (component.types.indexOf("street_number") >= 0) {
22918                 result.streetNumber = component.short_name;
22919             } else if (component.types.indexOf("route") >= 0) {
22920                 result.streetName = component.short_name;
22921             } else if (component.types.indexOf("neighborhood") >= 0) {
22922                 result.city = component.short_name;
22923             } else if (component.types.indexOf("locality") >= 0) {
22924                 result.city = component.short_name;
22925             } else if (component.types.indexOf("sublocality") >= 0) {
22926                 result.district = component.short_name;
22927             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22928                 result.stateOrProvince = component.short_name;
22929             } else if (component.types.indexOf("country") >= 0) {
22930                 result.country = component.short_name;
22931             }
22932         }
22933         
22934         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22935         result.addressLine2 = "";
22936         return result;
22937     },
22938     
22939     setZoomLevel: function(zoom)
22940     {
22941         this.gMapContext.map.setZoom(zoom);
22942     },
22943     
22944     show: function()
22945     {
22946         if(!this.el){
22947             return;
22948         }
22949         
22950         this.el.show();
22951         
22952         this.resize();
22953         
22954         this.fireEvent('show', this);
22955     },
22956     
22957     hide: function()
22958     {
22959         if(!this.el){
22960             return;
22961         }
22962         
22963         this.el.hide();
22964         
22965         this.fireEvent('hide', this);
22966     }
22967     
22968 });
22969
22970 Roo.apply(Roo.bootstrap.LocationPicker, {
22971     
22972     OverlayView : function(map, options)
22973     {
22974         options = options || {};
22975         
22976         this.setMap(map);
22977     }
22978     
22979     
22980 });/*
22981  * - LGPL
22982  *
22983  * Alert
22984  * 
22985  */
22986
22987 /**
22988  * @class Roo.bootstrap.Alert
22989  * @extends Roo.bootstrap.Component
22990  * Bootstrap Alert class
22991  * @cfg {String} title The title of alert
22992  * @cfg {String} html The content of alert
22993  * @cfg {String} weight (  success | info | warning | danger )
22994  * @cfg {String} faicon font-awesomeicon
22995  * 
22996  * @constructor
22997  * Create a new alert
22998  * @param {Object} config The config object
22999  */
23000
23001
23002 Roo.bootstrap.Alert = function(config){
23003     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23004     
23005 };
23006
23007 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23008     
23009     title: '',
23010     html: '',
23011     weight: false,
23012     faicon: false,
23013     
23014     getAutoCreate : function()
23015     {
23016         
23017         var cfg = {
23018             tag : 'div',
23019             cls : 'alert',
23020             cn : [
23021                 {
23022                     tag : 'i',
23023                     cls : 'roo-alert-icon'
23024                     
23025                 },
23026                 {
23027                     tag : 'b',
23028                     cls : 'roo-alert-title',
23029                     html : this.title
23030                 },
23031                 {
23032                     tag : 'span',
23033                     cls : 'roo-alert-text',
23034                     html : this.html
23035                 }
23036             ]
23037         };
23038         
23039         if(this.faicon){
23040             cfg.cn[0].cls += ' fa ' + this.faicon;
23041         }
23042         
23043         if(this.weight){
23044             cfg.cls += ' alert-' + this.weight;
23045         }
23046         
23047         return cfg;
23048     },
23049     
23050     initEvents: function() 
23051     {
23052         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23053     },
23054     
23055     setTitle : function(str)
23056     {
23057         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23058     },
23059     
23060     setText : function(str)
23061     {
23062         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23063     },
23064     
23065     setWeight : function(weight)
23066     {
23067         if(this.weight){
23068             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23069         }
23070         
23071         this.weight = weight;
23072         
23073         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23074     },
23075     
23076     setIcon : function(icon)
23077     {
23078         if(this.faicon){
23079             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23080         }
23081         
23082         this.faicon = icon
23083         
23084         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23085     },
23086     
23087     hide: function() 
23088     {
23089         this.el.hide();   
23090     },
23091     
23092     show: function() 
23093     {  
23094         this.el.show();   
23095     }
23096     
23097 });
23098
23099