roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         this.el.removeClass('hidden');
350     },
351     /**
352      * Hide a component - adds 'hidden' class
353      */
354     hide: function()
355     {
356         if (!this.el.hasClass('hidden')) {
357             this.el.addClass('hidden');
358         }
359         
360     }
361 });
362
363  /*
364  * - LGPL
365  *
366  * Body
367  * 
368  */
369
370 /**
371  * @class Roo.bootstrap.Body
372  * @extends Roo.bootstrap.Component
373  * Bootstrap Body class
374  * 
375  * @constructor
376  * Create a new body
377  * @param {Object} config The config object
378  */
379
380 Roo.bootstrap.Body = function(config){
381     Roo.bootstrap.Body.superclass.constructor.call(this, config);
382     this.el = Roo.get(document.body);
383     if (this.cls && this.cls.length) {
384         Roo.get(document.body).addClass(this.cls);
385     }
386 };
387
388 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
389       
390         autoCreate : {
391         cls: 'container'
392     },
393     onRender : function(ct, position)
394     {
395        /* Roo.log("Roo.bootstrap.Body - onRender");
396         if (this.cls && this.cls.length) {
397             Roo.get(document.body).addClass(this.cls);
398         }
399         // style??? xttr???
400         */
401     }
402     
403     
404  
405    
406 });
407
408  /*
409  * - LGPL
410  *
411  * button group
412  * 
413  */
414
415
416 /**
417  * @class Roo.bootstrap.ButtonGroup
418  * @extends Roo.bootstrap.Component
419  * Bootstrap ButtonGroup class
420  * @cfg {String} size lg | sm | xs (default empty normal)
421  * @cfg {String} align vertical | justified  (default none)
422  * @cfg {String} direction up | down (default down)
423  * @cfg {Boolean} toolbar false | true
424  * @cfg {Boolean} btn true | false
425  * 
426  * 
427  * @constructor
428  * Create a new Input
429  * @param {Object} config The config object
430  */
431
432 Roo.bootstrap.ButtonGroup = function(config){
433     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
434 };
435
436 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
437     
438     size: '',
439     align: '',
440     direction: '',
441     toolbar: false,
442     btn: true,
443
444     getAutoCreate : function(){
445         var cfg = {
446             cls: 'btn-group',
447             html : null
448         }
449         
450         cfg.html = this.html || cfg.html;
451         
452         if (this.toolbar) {
453             cfg = {
454                 cls: 'btn-toolbar',
455                 html: null
456             }
457             
458             return cfg;
459         }
460         
461         if (['vertical','justified'].indexOf(this.align)!==-1) {
462             cfg.cls = 'btn-group-' + this.align;
463             
464             if (this.align == 'justified') {
465                 console.log(this.items);
466             }
467         }
468         
469         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
470             cfg.cls += ' btn-group-' + this.size;
471         }
472         
473         if (this.direction == 'up') {
474             cfg.cls += ' dropup' ;
475         }
476         
477         return cfg;
478     }
479    
480 });
481
482  /*
483  * - LGPL
484  *
485  * button
486  * 
487  */
488
489 /**
490  * @class Roo.bootstrap.Button
491  * @extends Roo.bootstrap.Component
492  * Bootstrap Button class
493  * @cfg {String} html The button content
494  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
495  * @cfg {String} size ( lg | sm | xs)
496  * @cfg {String} tag ( a | input | submit)
497  * @cfg {String} href empty or href
498  * @cfg {Boolean} disabled default false;
499  * @cfg {Boolean} isClose default false;
500  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
501  * @cfg {String} badge text for badge
502  * @cfg {String} theme default 
503  * @cfg {Boolean} inverse 
504  * @cfg {Boolean} toggle 
505  * @cfg {String} ontext text for on toggle state
506  * @cfg {String} offtext text for off toggle state
507  * @cfg {Boolean} defaulton 
508  * @cfg {Boolean} preventDefault  default true
509  * @cfg {Boolean} removeClass remove the standard class..
510  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
511  * 
512  * @constructor
513  * Create a new button
514  * @param {Object} config The config object
515  */
516
517
518 Roo.bootstrap.Button = function(config){
519     Roo.bootstrap.Button.superclass.constructor.call(this, config);
520     this.addEvents({
521         // raw events
522         /**
523          * @event click
524          * When a butotn is pressed
525          * @param {Roo.bootstrap.Button} this
526          * @param {Roo.EventObject} e
527          */
528         "click" : true,
529          /**
530          * @event toggle
531          * After the button has been toggles
532          * @param {Roo.EventObject} e
533          * @param {boolean} pressed (also available as button.pressed)
534          */
535         "toggle" : true
536     });
537 };
538
539 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
540     html: false,
541     active: false,
542     weight: '',
543     size: '',
544     tag: 'button',
545     href: '',
546     disabled: false,
547     isClose: false,
548     glyphicon: '',
549     badge: '',
550     theme: 'default',
551     inverse: false,
552     
553     toggle: false,
554     ontext: 'ON',
555     offtext: 'OFF',
556     defaulton: true,
557     preventDefault: true,
558     removeClass: false,
559     name: false,
560     target: false,
561     
562     
563     pressed : null,
564      
565     
566     getAutoCreate : function(){
567         
568         var cfg = {
569             tag : 'button',
570             cls : 'roo-button',
571             html: ''
572         };
573         
574         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
575             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
576             this.tag = 'button';
577         } else {
578             cfg.tag = this.tag;
579         }
580         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
581         
582         if (this.toggle == true) {
583             cfg={
584                 tag: 'div',
585                 cls: 'slider-frame roo-button',
586                 cn: [
587                     {
588                         tag: 'span',
589                         'data-on-text':'ON',
590                         'data-off-text':'OFF',
591                         cls: 'slider-button',
592                         html: this.offtext
593                     }
594                 ]
595             };
596             
597             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
598                 cfg.cls += ' '+this.weight;
599             }
600             
601             return cfg;
602         }
603         
604         if (this.isClose) {
605             cfg.cls += ' close';
606             
607             cfg["aria-hidden"] = true;
608             
609             cfg.html = "&times;";
610             
611             return cfg;
612         }
613         
614          
615         if (this.theme==='default') {
616             cfg.cls = 'btn roo-button';
617             
618             //if (this.parentType != 'Navbar') {
619             this.weight = this.weight.length ?  this.weight : 'default';
620             //}
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 
623                 cfg.cls += ' btn-' + this.weight;
624             }
625         } else if (this.theme==='glow') {
626             
627             cfg.tag = 'a';
628             cfg.cls = 'btn-glow roo-button';
629             
630             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
631                 
632                 cfg.cls += ' ' + this.weight;
633             }
634         }
635    
636         
637         if (this.inverse) {
638             this.cls += ' inverse';
639         }
640         
641         
642         if (this.active) {
643             cfg.cls += ' active';
644         }
645         
646         if (this.disabled) {
647             cfg.disabled = 'disabled';
648         }
649         
650         if (this.items) {
651             Roo.log('changing to ul' );
652             cfg.tag = 'ul';
653             this.glyphicon = 'caret';
654         }
655         
656         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
657          
658         //gsRoo.log(this.parentType);
659         if (this.parentType === 'Navbar' && !this.parent().bar) {
660             Roo.log('changing to li?');
661             
662             cfg.tag = 'li';
663             
664             cfg.cls = '';
665             cfg.cn =  [{
666                 tag : 'a',
667                 cls : 'roo-button',
668                 html : this.html,
669                 href : this.href || '#'
670             }];
671             if (this.menu) {
672                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
673                 cfg.cls += ' dropdown';
674             }   
675             
676             delete cfg.html;
677             
678         }
679         
680        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
681         
682         if (this.glyphicon) {
683             cfg.html = ' ' + cfg.html;
684             
685             cfg.cn = [
686                 {
687                     tag: 'span',
688                     cls: 'glyphicon glyphicon-' + this.glyphicon
689                 }
690             ];
691         }
692         
693         if (this.badge) {
694             cfg.html += ' ';
695             
696             cfg.tag = 'a';
697             
698 //            cfg.cls='btn roo-button';
699             
700             cfg.href=this.href;
701             
702             var value = cfg.html;
703             
704             if(this.glyphicon){
705                 value = {
706                             tag: 'span',
707                             cls: 'glyphicon glyphicon-' + this.glyphicon,
708                             html: this.html
709                         };
710                 
711             }
712             
713             cfg.cn = [
714                 value,
715                 {
716                     tag: 'span',
717                     cls: 'badge',
718                     html: this.badge
719                 }
720             ];
721             
722             cfg.html='';
723         }
724         
725         if (this.menu) {
726             cfg.cls += ' dropdown';
727             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
728         }
729         
730         if (cfg.tag !== 'a' && this.href !== '') {
731             throw "Tag must be a to set href.";
732         } else if (this.href.length > 0) {
733             cfg.href = this.href;
734         }
735         
736         if(this.removeClass){
737             cfg.cls = '';
738         }
739         
740         if(this.target){
741             cfg.target = this.target;
742         }
743         
744         return cfg;
745     },
746     initEvents: function() {
747        // Roo.log('init events?');
748 //        Roo.log(this.el.dom);
749         // add the menu...
750         
751         if (typeof (this.menu) != 'undefined') {
752             this.menu.parentType = this.xtype;
753             this.menu.triggerEl = this.el;
754             this.addxtype(Roo.apply({}, this.menu));
755         }
756
757
758        if (this.el.hasClass('roo-button')) {
759             this.el.on('click', this.onClick, this);
760        } else {
761             this.el.select('.roo-button').on('click', this.onClick, this);
762        }
763        
764        if(this.removeClass){
765            this.el.on('click', this.onClick, this);
766        }
767        
768        this.el.enableDisplayMode();
769         
770     },
771     onClick : function(e)
772     {
773         if (this.disabled) {
774             return;
775         }
776         
777         
778         Roo.log('button on click ');
779         if(this.preventDefault){
780             e.preventDefault();
781         }
782         if (this.pressed === true || this.pressed === false) {
783             this.pressed = !this.pressed;
784             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
785             this.fireEvent('toggle', this, e, this.pressed);
786         }
787         
788         
789         this.fireEvent('click', this, e);
790     },
791     
792     /**
793      * Enables this button
794      */
795     enable : function()
796     {
797         this.disabled = false;
798         this.el.removeClass('disabled');
799     },
800     
801     /**
802      * Disable this button
803      */
804     disable : function()
805     {
806         this.disabled = true;
807         this.el.addClass('disabled');
808     },
809      /**
810      * sets the active state on/off, 
811      * @param {Boolean} state (optional) Force a particular state
812      */
813     setActive : function(v) {
814         
815         this.el[v ? 'addClass' : 'removeClass']('active');
816     },
817      /**
818      * toggles the current active state 
819      */
820     toggleActive : function()
821     {
822        var active = this.el.hasClass('active');
823        this.setActive(!active);
824        
825         
826     },
827     setText : function(str)
828     {
829         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
830     },
831     getText : function()
832     {
833         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
834     },
835     hide: function() {
836        
837      
838         this.el.hide();   
839     },
840     show: function() {
841        
842         this.el.show();   
843     }
844     
845     
846 });
847
848  /*
849  * - LGPL
850  *
851  * column
852  * 
853  */
854
855 /**
856  * @class Roo.bootstrap.Column
857  * @extends Roo.bootstrap.Component
858  * Bootstrap Column class
859  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
860  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
861  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
862  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
863  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
864  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
865  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
866  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
867  *
868  * 
869  * @cfg {Boolean} hidden (true|false) hide the element
870  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
871  * @cfg {String} fa (ban|check|...) font awesome icon
872  * @cfg {Number} fasize (1|2|....) font awsome size
873
874  * @cfg {String} icon (info-sign|check|...) glyphicon name
875
876  * @cfg {String} html content of column.
877  * 
878  * @constructor
879  * Create a new Column
880  * @param {Object} config The config object
881  */
882
883 Roo.bootstrap.Column = function(config){
884     Roo.bootstrap.Column.superclass.constructor.call(this, config);
885 };
886
887 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
888     
889     xs: false,
890     sm: false,
891     md: false,
892     lg: false,
893     xsoff: false,
894     smoff: false,
895     mdoff: false,
896     lgoff: false,
897     html: '',
898     offset: 0,
899     alert: false,
900     fa: false,
901     icon : false,
902     hidden : false,
903     fasize : 1,
904     
905     getAutoCreate : function(){
906         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
907         
908         cfg = {
909             tag: 'div',
910             cls: 'column'
911         };
912         
913         var settings=this;
914         ['xs','sm','md','lg'].map(function(size){
915             //Roo.log( size + ':' + settings[size]);
916             
917             if (settings[size+'off'] !== false) {
918                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
919             }
920             
921             if (settings[size] === false) {
922                 return;
923             }
924             Roo.log(settings[size]);
925             if (!settings[size]) { // 0 = hidden
926                 cfg.cls += ' hidden-' + size;
927                 return;
928             }
929             cfg.cls += ' col-' + size + '-' + settings[size];
930             
931         });
932         
933         if (this.hidden) {
934             cfg.cls += ' hidden';
935         }
936         
937         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
938             cfg.cls +=' alert alert-' + this.alert;
939         }
940         
941         
942         if (this.html.length) {
943             cfg.html = this.html;
944         }
945         if (this.fa) {
946             var fasize = '';
947             if (this.fasize > 1) {
948                 fasize = ' fa-' + this.fasize + 'x';
949             }
950             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
951             
952             
953         }
954         if (this.icon) {
955             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  
964
965  /*
966  * - LGPL
967  *
968  * page container.
969  * 
970  */
971
972
973 /**
974  * @class Roo.bootstrap.Container
975  * @extends Roo.bootstrap.Component
976  * Bootstrap Container class
977  * @cfg {Boolean} jumbotron is it a jumbotron element
978  * @cfg {String} html content of element
979  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
980  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
981  * @cfg {String} header content of header (for panel)
982  * @cfg {String} footer content of footer (for panel)
983  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
984  * @cfg {String} tag (header|aside|section) type of HTML tag.
985  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
986  * @cfg {String} fa (ban|check|...) font awesome icon
987  * @cfg {String} icon (info-sign|check|...) glyphicon name
988  * @cfg {Boolean} hidden (true|false) hide the element
989
990  *     
991  * @constructor
992  * Create a new Container
993  * @param {Object} config The config object
994  */
995
996 Roo.bootstrap.Container = function(config){
997     Roo.bootstrap.Container.superclass.constructor.call(this, config);
998 };
999
1000 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1001     
1002     jumbotron : false,
1003     well: '',
1004     panel : '',
1005     header: '',
1006     footer : '',
1007     sticky: '',
1008     tag : false,
1009     alert : false,
1010     fa: false,
1011     icon : false,
1012   
1013      
1014     getChildContainer : function() {
1015         
1016         if(!this.el){
1017             return false;
1018         }
1019         
1020         if (this.panel.length) {
1021             return this.el.select('.panel-body',true).first();
1022         }
1023         
1024         return this.el;
1025     },
1026     
1027     
1028     getAutoCreate : function(){
1029         
1030         var cfg = {
1031             tag : this.tag || 'div',
1032             html : '',
1033             cls : ''
1034         };
1035         if (this.jumbotron) {
1036             cfg.cls = 'jumbotron';
1037         }
1038         
1039         
1040         
1041         // - this is applied by the parent..
1042         //if (this.cls) {
1043         //    cfg.cls = this.cls + '';
1044         //}
1045         
1046         if (this.sticky.length) {
1047             
1048             var bd = Roo.get(document.body);
1049             if (!bd.hasClass('bootstrap-sticky')) {
1050                 bd.addClass('bootstrap-sticky');
1051                 Roo.select('html',true).setStyle('height', '100%');
1052             }
1053              
1054             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1055         }
1056         
1057         
1058         if (this.well.length) {
1059             switch (this.well) {
1060                 case 'lg':
1061                 case 'sm':
1062                     cfg.cls +=' well well-' +this.well;
1063                     break;
1064                 default:
1065                     cfg.cls +=' well';
1066                     break;
1067             }
1068         }
1069         
1070         if (this.hidden) {
1071             cfg.cls += ' hidden';
1072         }
1073         
1074         
1075         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1076             cfg.cls +=' alert alert-' + this.alert;
1077         }
1078         
1079         var body = cfg;
1080         
1081         if (this.panel.length) {
1082             cfg.cls += ' panel panel-' + this.panel;
1083             cfg.cn = [];
1084             if (this.header.length) {
1085                 cfg.cn.push({
1086                     
1087                     cls : 'panel-heading',
1088                     cn : [{
1089                         tag: 'h3',
1090                         cls : 'panel-title',
1091                         html : this.header
1092                     }]
1093                     
1094                 });
1095             }
1096             body = false;
1097             cfg.cn.push({
1098                 cls : 'panel-body',
1099                 html : this.html
1100             });
1101             
1102             
1103             if (this.footer.length) {
1104                 cfg.cn.push({
1105                     cls : 'panel-footer',
1106                     html : this.footer
1107                     
1108                 });
1109             }
1110             
1111         }
1112         
1113         if (body) {
1114             body.html = this.html || cfg.html;
1115             // prefix with the icons..
1116             if (this.fa) {
1117                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1118             }
1119             if (this.icon) {
1120                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1121             }
1122             
1123             
1124         }
1125         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1126             cfg.cls =  'container';
1127         }
1128         
1129         return cfg;
1130     },
1131     
1132     titleEl : function()
1133     {
1134         if(!this.el || !this.panel.length || !this.header.length){
1135             return;
1136         }
1137         
1138         return this.el.select('.panel-title',true).first();
1139     },
1140     
1141     setTitle : function(v)
1142     {
1143         var titleEl = this.titleEl();
1144         
1145         if(!titleEl){
1146             return;
1147         }
1148         
1149         titleEl.dom.innerHTML = v;
1150     },
1151     
1152     getTitle : function()
1153     {
1154         
1155         var titleEl = this.titleEl();
1156         
1157         if(!titleEl){
1158             return '';
1159         }
1160         
1161         return titleEl.dom.innerHTML;
1162     }
1163     
1164     
1165    
1166 });
1167
1168  /*
1169  * - LGPL
1170  *
1171  * image
1172  * 
1173  */
1174
1175
1176 /**
1177  * @class Roo.bootstrap.Img
1178  * @extends Roo.bootstrap.Component
1179  * Bootstrap Img class
1180  * @cfg {Boolean} imgResponsive false | true
1181  * @cfg {String} border rounded | circle | thumbnail
1182  * @cfg {String} src image source
1183  * @cfg {String} alt image alternative text
1184  * @cfg {String} href a tag href
1185  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1186  * 
1187  * @constructor
1188  * Create a new Input
1189  * @param {Object} config The config object
1190  */
1191
1192 Roo.bootstrap.Img = function(config){
1193     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1194     
1195     this.addEvents({
1196         // img events
1197         /**
1198          * @event click
1199          * The img click event for the img.
1200          * @param {Roo.EventObject} e
1201          */
1202         "click" : true
1203     });
1204 };
1205
1206 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1207     
1208     imgResponsive: true,
1209     border: '',
1210     src: '',
1211     href: false,
1212     target: false,
1213
1214     getAutoCreate : function(){
1215         
1216         var cfg = {
1217             tag: 'img',
1218             cls: (this.imgResponsive) ? 'img-responsive' : '',
1219             html : null
1220         }
1221         
1222         cfg.html = this.html || cfg.html;
1223         
1224         cfg.src = this.src || cfg.src;
1225         
1226         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1227             cfg.cls += ' img-' + this.border;
1228         }
1229         
1230         if(this.alt){
1231             cfg.alt = this.alt;
1232         }
1233         
1234         if(this.href){
1235             var a = {
1236                 tag: 'a',
1237                 href: this.href,
1238                 cn: [
1239                     cfg
1240                 ]
1241             }
1242             
1243             if(this.target){
1244                 a.target = this.target;
1245             }
1246             
1247         }
1248         
1249         
1250         return (this.href) ? a : cfg;
1251     },
1252     
1253     initEvents: function() {
1254         
1255         if(!this.href){
1256             this.el.on('click', this.onClick, this);
1257         }
1258     },
1259     
1260     onClick : function(e)
1261     {
1262         Roo.log('img onclick');
1263         this.fireEvent('click', this, e);
1264     }
1265    
1266 });
1267
1268  /*
1269  * - LGPL
1270  *
1271  * image
1272  * 
1273  */
1274
1275
1276 /**
1277  * @class Roo.bootstrap.Link
1278  * @extends Roo.bootstrap.Component
1279  * Bootstrap Link Class
1280  * @cfg {String} alt image alternative text
1281  * @cfg {String} href a tag href
1282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1283  * @cfg {String} html the content of the link.
1284  * @cfg {String} anchor name for the anchor link
1285
1286  * @cfg {Boolean} preventDefault (true | false) default false
1287
1288  * 
1289  * @constructor
1290  * Create a new Input
1291  * @param {Object} config The config object
1292  */
1293
1294 Roo.bootstrap.Link = function(config){
1295     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1296     
1297     this.addEvents({
1298         // img events
1299         /**
1300          * @event click
1301          * The img click event for the img.
1302          * @param {Roo.EventObject} e
1303          */
1304         "click" : true
1305     });
1306 };
1307
1308 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1309     
1310     href: false,
1311     target: false,
1312     preventDefault: false,
1313     anchor : false,
1314     alt : false,
1315
1316     getAutoCreate : function()
1317     {
1318         
1319         var cfg = {
1320             tag: 'a'
1321         };
1322         // anchor's do not require html/href...
1323         if (this.anchor === false) {
1324             cfg.html = this.html || 'html-missing';
1325             cfg.href = this.href || '#';
1326         } else {
1327             cfg.name = this.anchor;
1328             if (this.html !== false) {
1329                 cfg.html = this.html;
1330             }
1331             if (this.href !== false) {
1332                 cfg.href = this.href;
1333             }
1334         }
1335         
1336         if(this.alt !== false){
1337             cfg.alt = this.alt;
1338         }
1339         
1340         
1341         if(this.target !== false) {
1342             cfg.target = this.target;
1343         }
1344         
1345         return cfg;
1346     },
1347     
1348     initEvents: function() {
1349         
1350         if(!this.href || this.preventDefault){
1351             this.el.on('click', this.onClick, this);
1352         }
1353     },
1354     
1355     onClick : function(e)
1356     {
1357         if(this.preventDefault){
1358             e.preventDefault();
1359         }
1360         //Roo.log('img onclick');
1361         this.fireEvent('click', this, e);
1362     }
1363    
1364 });
1365
1366  /*
1367  * - LGPL
1368  *
1369  * header
1370  * 
1371  */
1372
1373 /**
1374  * @class Roo.bootstrap.Header
1375  * @extends Roo.bootstrap.Component
1376  * Bootstrap Header class
1377  * @cfg {String} html content of header
1378  * @cfg {Number} level (1|2|3|4|5|6) default 1
1379  * 
1380  * @constructor
1381  * Create a new Header
1382  * @param {Object} config The config object
1383  */
1384
1385
1386 Roo.bootstrap.Header  = function(config){
1387     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1391     
1392     //href : false,
1393     html : false,
1394     level : 1,
1395     
1396     
1397     
1398     getAutoCreate : function(){
1399         
1400         
1401         
1402         var cfg = {
1403             tag: 'h' + (1 *this.level),
1404             html: this.html || ''
1405         } ;
1406         
1407         return cfg;
1408     }
1409    
1410 });
1411
1412  
1413
1414  /*
1415  * Based on:
1416  * Ext JS Library 1.1.1
1417  * Copyright(c) 2006-2007, Ext JS, LLC.
1418  *
1419  * Originally Released Under LGPL - original licence link has changed is not relivant.
1420  *
1421  * Fork - LGPL
1422  * <script type="text/javascript">
1423  */
1424  
1425 /**
1426  * @class Roo.bootstrap.MenuMgr
1427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1428  * @singleton
1429  */
1430 Roo.bootstrap.MenuMgr = function(){
1431    var menus, active, groups = {}, attached = false, lastShow = new Date();
1432
1433    // private - called when first menu is created
1434    function init(){
1435        menus = {};
1436        active = new Roo.util.MixedCollection();
1437        Roo.get(document).addKeyListener(27, function(){
1438            if(active.length > 0){
1439                hideAll();
1440            }
1441        });
1442    }
1443
1444    // private
1445    function hideAll(){
1446        if(active && active.length > 0){
1447            var c = active.clone();
1448            c.each(function(m){
1449                m.hide();
1450            });
1451        }
1452    }
1453
1454    // private
1455    function onHide(m){
1456        active.remove(m);
1457        if(active.length < 1){
1458            Roo.get(document).un("mouseup", onMouseDown);
1459             
1460            attached = false;
1461        }
1462    }
1463
1464    // private
1465    function onShow(m){
1466        var last = active.last();
1467        lastShow = new Date();
1468        active.add(m);
1469        if(!attached){
1470           Roo.get(document).on("mouseup", onMouseDown);
1471            
1472            attached = true;
1473        }
1474        if(m.parentMenu){
1475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1476           m.parentMenu.activeChild = m;
1477        }else if(last && last.isVisible()){
1478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1479        }
1480    }
1481
1482    // private
1483    function onBeforeHide(m){
1484        if(m.activeChild){
1485            m.activeChild.hide();
1486        }
1487        if(m.autoHideTimer){
1488            clearTimeout(m.autoHideTimer);
1489            delete m.autoHideTimer;
1490        }
1491    }
1492
1493    // private
1494    function onBeforeShow(m){
1495        var pm = m.parentMenu;
1496        if(!pm && !m.allowOtherMenus){
1497            hideAll();
1498        }else if(pm && pm.activeChild && active != m){
1499            pm.activeChild.hide();
1500        }
1501    }
1502
1503    // private
1504    function onMouseDown(e){
1505         Roo.log("on MouseDown");
1506         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1507            hideAll();
1508         }
1509         
1510         
1511    }
1512
1513    // private
1514    function onBeforeCheck(mi, state){
1515        if(state){
1516            var g = groups[mi.group];
1517            for(var i = 0, l = g.length; i < l; i++){
1518                if(g[i] != mi){
1519                    g[i].setChecked(false);
1520                }
1521            }
1522        }
1523    }
1524
1525    return {
1526
1527        /**
1528         * Hides all menus that are currently visible
1529         */
1530        hideAll : function(){
1531             hideAll();  
1532        },
1533
1534        // private
1535        register : function(menu){
1536            if(!menus){
1537                init();
1538            }
1539            menus[menu.id] = menu;
1540            menu.on("beforehide", onBeforeHide);
1541            menu.on("hide", onHide);
1542            menu.on("beforeshow", onBeforeShow);
1543            menu.on("show", onShow);
1544            var g = menu.group;
1545            if(g && menu.events["checkchange"]){
1546                if(!groups[g]){
1547                    groups[g] = [];
1548                }
1549                groups[g].push(menu);
1550                menu.on("checkchange", onCheck);
1551            }
1552        },
1553
1554         /**
1555          * Returns a {@link Roo.menu.Menu} object
1556          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1557          * be used to generate and return a new Menu instance.
1558          */
1559        get : function(menu){
1560            if(typeof menu == "string"){ // menu id
1561                return menus[menu];
1562            }else if(menu.events){  // menu instance
1563                return menu;
1564            }
1565            /*else if(typeof menu.length == 'number'){ // array of menu items?
1566                return new Roo.bootstrap.Menu({items:menu});
1567            }else{ // otherwise, must be a config
1568                return new Roo.bootstrap.Menu(menu);
1569            }
1570            */
1571            return false;
1572        },
1573
1574        // private
1575        unregister : function(menu){
1576            delete menus[menu.id];
1577            menu.un("beforehide", onBeforeHide);
1578            menu.un("hide", onHide);
1579            menu.un("beforeshow", onBeforeShow);
1580            menu.un("show", onShow);
1581            var g = menu.group;
1582            if(g && menu.events["checkchange"]){
1583                groups[g].remove(menu);
1584                menu.un("checkchange", onCheck);
1585            }
1586        },
1587
1588        // private
1589        registerCheckable : function(menuItem){
1590            var g = menuItem.group;
1591            if(g){
1592                if(!groups[g]){
1593                    groups[g] = [];
1594                }
1595                groups[g].push(menuItem);
1596                menuItem.on("beforecheckchange", onBeforeCheck);
1597            }
1598        },
1599
1600        // private
1601        unregisterCheckable : function(menuItem){
1602            var g = menuItem.group;
1603            if(g){
1604                groups[g].remove(menuItem);
1605                menuItem.un("beforecheckchange", onBeforeCheck);
1606            }
1607        }
1608    };
1609 }();/*
1610  * - LGPL
1611  *
1612  * menu
1613  * 
1614  */
1615
1616 /**
1617  * @class Roo.bootstrap.Menu
1618  * @extends Roo.bootstrap.Component
1619  * Bootstrap Menu class - container for MenuItems
1620  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1621  * 
1622  * @constructor
1623  * Create a new Menu
1624  * @param {Object} config The config object
1625  */
1626
1627
1628 Roo.bootstrap.Menu = function(config){
1629     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1630     if (this.registerMenu) {
1631         Roo.bootstrap.MenuMgr.register(this);
1632     }
1633     this.addEvents({
1634         /**
1635          * @event beforeshow
1636          * Fires before this menu is displayed
1637          * @param {Roo.menu.Menu} this
1638          */
1639         beforeshow : true,
1640         /**
1641          * @event beforehide
1642          * Fires before this menu is hidden
1643          * @param {Roo.menu.Menu} this
1644          */
1645         beforehide : true,
1646         /**
1647          * @event show
1648          * Fires after this menu is displayed
1649          * @param {Roo.menu.Menu} this
1650          */
1651         show : true,
1652         /**
1653          * @event hide
1654          * Fires after this menu is hidden
1655          * @param {Roo.menu.Menu} this
1656          */
1657         hide : true,
1658         /**
1659          * @event click
1660          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1661          * @param {Roo.menu.Menu} this
1662          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1663          * @param {Roo.EventObject} e
1664          */
1665         click : true,
1666         /**
1667          * @event mouseover
1668          * Fires when the mouse is hovering over this menu
1669          * @param {Roo.menu.Menu} this
1670          * @param {Roo.EventObject} e
1671          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1672          */
1673         mouseover : true,
1674         /**
1675          * @event mouseout
1676          * Fires when the mouse exits this menu
1677          * @param {Roo.menu.Menu} this
1678          * @param {Roo.EventObject} e
1679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1680          */
1681         mouseout : true,
1682         /**
1683          * @event itemclick
1684          * Fires when a menu item contained in this menu is clicked
1685          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1686          * @param {Roo.EventObject} e
1687          */
1688         itemclick: true
1689     });
1690     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1691 };
1692
1693 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1694     
1695    /// html : false,
1696     //align : '',
1697     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1698     type: false,
1699     /**
1700      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1701      */
1702     registerMenu : true,
1703     
1704     menuItems :false, // stores the menu items..
1705     
1706     hidden:true,
1707     
1708     parentMenu : false,
1709     
1710     getChildContainer : function() {
1711         return this.el;  
1712     },
1713     
1714     getAutoCreate : function(){
1715          
1716         //if (['right'].indexOf(this.align)!==-1) {
1717         //    cfg.cn[1].cls += ' pull-right'
1718         //}
1719         
1720         
1721         var cfg = {
1722             tag : 'ul',
1723             cls : 'dropdown-menu' ,
1724             style : 'z-index:1000'
1725             
1726         }
1727         
1728         if (this.type === 'submenu') {
1729             cfg.cls = 'submenu active';
1730         }
1731         if (this.type === 'treeview') {
1732             cfg.cls = 'treeview-menu';
1733         }
1734         
1735         return cfg;
1736     },
1737     initEvents : function() {
1738         
1739        // Roo.log("ADD event");
1740        // Roo.log(this.triggerEl.dom);
1741         this.triggerEl.on('click', this.onTriggerPress, this);
1742         this.triggerEl.addClass('dropdown-toggle');
1743         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1744
1745         this.el.on("mouseover", this.onMouseOver, this);
1746         this.el.on("mouseout", this.onMouseOut, this);
1747         
1748         
1749     },
1750     findTargetItem : function(e){
1751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1752         if(!t){
1753             return false;
1754         }
1755         //Roo.log(t);         Roo.log(t.id);
1756         if(t && t.id){
1757             //Roo.log(this.menuitems);
1758             return this.menuitems.get(t.id);
1759             
1760             //return this.items.get(t.menuItemId);
1761         }
1762         
1763         return false;
1764     },
1765     onClick : function(e){
1766         Roo.log("menu.onClick");
1767         var t = this.findTargetItem(e);
1768         if(!t || t.isContainer){
1769             return;
1770         }
1771         Roo.log(e);
1772         /*
1773         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1774             if(t == this.activeItem && t.shouldDeactivate(e)){
1775                 this.activeItem.deactivate();
1776                 delete this.activeItem;
1777                 return;
1778             }
1779             if(t.canActivate){
1780                 this.setActiveItem(t, true);
1781             }
1782             return;
1783             
1784             
1785         }
1786         */
1787        
1788         Roo.log('pass click event');
1789         
1790         t.onClick(e);
1791         
1792         this.fireEvent("click", this, t, e);
1793         
1794         this.hide();
1795     },
1796      onMouseOver : function(e){
1797         var t  = this.findTargetItem(e);
1798         //Roo.log(t);
1799         //if(t){
1800         //    if(t.canActivate && !t.disabled){
1801         //        this.setActiveItem(t, true);
1802         //    }
1803         //}
1804         
1805         this.fireEvent("mouseover", this, e, t);
1806     },
1807     isVisible : function(){
1808         return !this.hidden;
1809     },
1810      onMouseOut : function(e){
1811         var t  = this.findTargetItem(e);
1812         
1813         //if(t ){
1814         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1815         //        this.activeItem.deactivate();
1816         //        delete this.activeItem;
1817         //    }
1818         //}
1819         this.fireEvent("mouseout", this, e, t);
1820     },
1821     
1822     
1823     /**
1824      * Displays this menu relative to another element
1825      * @param {String/HTMLElement/Roo.Element} element The element to align to
1826      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1827      * the element (defaults to this.defaultAlign)
1828      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1829      */
1830     show : function(el, pos, parentMenu){
1831         this.parentMenu = parentMenu;
1832         if(!this.el){
1833             this.render();
1834         }
1835         this.fireEvent("beforeshow", this);
1836         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1837     },
1838      /**
1839      * Displays this menu at a specific xy position
1840      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1841      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1842      */
1843     showAt : function(xy, parentMenu, /* private: */_e){
1844         this.parentMenu = parentMenu;
1845         if(!this.el){
1846             this.render();
1847         }
1848         if(_e !== false){
1849             this.fireEvent("beforeshow", this);
1850             //xy = this.el.adjustForConstraints(xy);
1851         }
1852         
1853         //this.el.show();
1854         this.hideMenuItems();
1855         this.hidden = false;
1856         this.triggerEl.addClass('open');
1857         
1858         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1859             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1860         }
1861         
1862         this.el.setXY(xy);
1863         this.focus();
1864         this.fireEvent("show", this);
1865     },
1866     
1867     focus : function(){
1868         return;
1869         if(!this.hidden){
1870             this.doFocus.defer(50, this);
1871         }
1872     },
1873
1874     doFocus : function(){
1875         if(!this.hidden){
1876             this.focusEl.focus();
1877         }
1878     },
1879
1880     /**
1881      * Hides this menu and optionally all parent menus
1882      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1883      */
1884     hide : function(deep){
1885         
1886         this.hideMenuItems();
1887         if(this.el && this.isVisible()){
1888             this.fireEvent("beforehide", this);
1889             if(this.activeItem){
1890                 this.activeItem.deactivate();
1891                 this.activeItem = null;
1892             }
1893             this.triggerEl.removeClass('open');;
1894             this.hidden = true;
1895             this.fireEvent("hide", this);
1896         }
1897         if(deep === true && this.parentMenu){
1898             this.parentMenu.hide(true);
1899         }
1900     },
1901     
1902     onTriggerPress  : function(e)
1903     {
1904         
1905         Roo.log('trigger press');
1906         //Roo.log(e.getTarget());
1907        // Roo.log(this.triggerEl.dom);
1908         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1909             return;
1910         }
1911         if (this.isVisible()) {
1912             Roo.log('hide');
1913             this.hide();
1914         } else {
1915             this.show(this.triggerEl, false, false);
1916         }
1917         
1918         
1919     },
1920     
1921          
1922        
1923     
1924     hideMenuItems : function()
1925     {
1926         //$(backdrop).remove()
1927         Roo.select('.open',true).each(function(aa) {
1928             
1929             aa.removeClass('open');
1930           //var parent = getParent($(this))
1931           //var relatedTarget = { relatedTarget: this }
1932           
1933            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1934           //if (e.isDefaultPrevented()) return
1935            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1936         })
1937     },
1938     addxtypeChild : function (tree, cntr) {
1939         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1940           
1941         this.menuitems.add(comp);
1942         return comp;
1943
1944     },
1945     getEl : function()
1946     {
1947         Roo.log(this.el);
1948         return this.el;
1949     }
1950 });
1951
1952  
1953  /*
1954  * - LGPL
1955  *
1956  * menu item
1957  * 
1958  */
1959
1960
1961 /**
1962  * @class Roo.bootstrap.MenuItem
1963  * @extends Roo.bootstrap.Component
1964  * Bootstrap MenuItem class
1965  * @cfg {String} html the menu label
1966  * @cfg {String} href the link
1967  * @cfg {Boolean} preventDefault (true | false) default true
1968  * @cfg {Boolean} isContainer (true | false) default false
1969  * 
1970  * 
1971  * @constructor
1972  * Create a new MenuItem
1973  * @param {Object} config The config object
1974  */
1975
1976
1977 Roo.bootstrap.MenuItem = function(config){
1978     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1979     this.addEvents({
1980         // raw events
1981         /**
1982          * @event click
1983          * The raw click event for the entire grid.
1984          * @param {Roo.bootstrap.MenuItem} this
1985          * @param {Roo.EventObject} e
1986          */
1987         "click" : true
1988     });
1989 };
1990
1991 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1992     
1993     href : false,
1994     html : false,
1995     preventDefault: true,
1996     isContainer : false,
1997     
1998     getAutoCreate : function(){
1999         
2000         if(this.isContainer){
2001             return {
2002                 tag: 'li',
2003                 cls: 'dropdown-menu-item'
2004             };
2005         }
2006         
2007         var cfg= {
2008             tag: 'li',
2009             cls: 'dropdown-menu-item',
2010             cn: [
2011                     {
2012                         tag : 'a',
2013                         href : '#',
2014                         html : 'Link'
2015                     }
2016                 ]
2017         };
2018         if (this.parent().type == 'treeview') {
2019             cfg.cls = 'treeview-menu';
2020         }
2021         
2022         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2023         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2024         return cfg;
2025     },
2026     
2027     initEvents: function() {
2028         
2029         //this.el.select('a').on('click', this.onClick, this);
2030         
2031     },
2032     onClick : function(e)
2033     {
2034         Roo.log('item on click ');
2035         //if(this.preventDefault){
2036         //    e.preventDefault();
2037         //}
2038         //this.parent().hideMenuItems();
2039         
2040         this.fireEvent('click', this, e);
2041     },
2042     getEl : function()
2043     {
2044         return this.el;
2045     }
2046 });
2047
2048  
2049
2050  /*
2051  * - LGPL
2052  *
2053  * menu separator
2054  * 
2055  */
2056
2057
2058 /**
2059  * @class Roo.bootstrap.MenuSeparator
2060  * @extends Roo.bootstrap.Component
2061  * Bootstrap MenuSeparator class
2062  * 
2063  * @constructor
2064  * Create a new MenuItem
2065  * @param {Object} config The config object
2066  */
2067
2068
2069 Roo.bootstrap.MenuSeparator = function(config){
2070     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2071 };
2072
2073 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2074     
2075     getAutoCreate : function(){
2076         var cfg = {
2077             cls: 'divider',
2078             tag : 'li'
2079         };
2080         
2081         return cfg;
2082     }
2083    
2084 });
2085
2086  
2087
2088  
2089 /*
2090 * Licence: LGPL
2091 */
2092
2093 /**
2094  * @class Roo.bootstrap.Modal
2095  * @extends Roo.bootstrap.Component
2096  * Bootstrap Modal class
2097  * @cfg {String} title Title of dialog
2098  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2099  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2100  * @cfg {Boolean} specificTitle default false
2101  * @cfg {Array} buttons Array of buttons or standard button set..
2102  * @cfg {String} buttonPosition (left|right|center) default right
2103  * @cfg {Boolean} animate default true
2104  * @cfg {Boolean} allow_close default true
2105  * 
2106  * @constructor
2107  * Create a new Modal Dialog
2108  * @param {Object} config The config object
2109  */
2110
2111 Roo.bootstrap.Modal = function(config){
2112     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2113     this.addEvents({
2114         // raw events
2115         /**
2116          * @event btnclick
2117          * The raw btnclick event for the button
2118          * @param {Roo.EventObject} e
2119          */
2120         "btnclick" : true
2121     });
2122     this.buttons = this.buttons || [];
2123      
2124     if (this.tmpl) {
2125         this.tmpl = Roo.factory(this.tmpl);
2126     }
2127     
2128 };
2129
2130 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2131     
2132     title : 'test dialog',
2133    
2134     buttons : false,
2135     
2136     // set on load...
2137      
2138     html: false,
2139     
2140     tmp: false,
2141     
2142     specificTitle: false,
2143     
2144     buttonPosition: 'right',
2145     
2146     allow_close : true,
2147     
2148     animate : true,
2149     
2150     
2151      // private
2152     bodyEl:  false,
2153     footerEl:  false,
2154     titleEl:  false,
2155     closeEl:  false,
2156     
2157     
2158     onRender : function(ct, position)
2159     {
2160         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2161      
2162         if(!this.el){
2163             var cfg = Roo.apply({},  this.getAutoCreate());
2164             cfg.id = Roo.id();
2165             //if(!cfg.name){
2166             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2167             //}
2168             //if (!cfg.name.length) {
2169             //    delete cfg.name;
2170            // }
2171             if (this.cls) {
2172                 cfg.cls += ' ' + this.cls;
2173             }
2174             if (this.style) {
2175                 cfg.style = this.style;
2176             }
2177             this.el = Roo.get(document.body).createChild(cfg, position);
2178         }
2179         //var type = this.el.dom.type;
2180         
2181         
2182         
2183         
2184         if(this.tabIndex !== undefined){
2185             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2186         }
2187         
2188         
2189         this.bodyEl = this.el.select('.modal-body',true).first();
2190         this.closeEl = this.el.select('.modal-header .close', true).first();
2191         this.footerEl = this.el.select('.modal-footer',true).first();
2192         this.titleEl = this.el.select('.modal-title',true).first();
2193         
2194         
2195          
2196         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2197         this.maskEl.enableDisplayMode("block");
2198         this.maskEl.hide();
2199         //this.el.addClass("x-dlg-modal");
2200     
2201         if (this.buttons.length) {
2202             Roo.each(this.buttons, function(bb) {
2203                 b = Roo.apply({}, bb);
2204                 b.xns = b.xns || Roo.bootstrap;
2205                 b.xtype = b.xtype || 'Button';
2206                 if (typeof(b.listeners) == 'undefined') {
2207                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2208                 }
2209                 
2210                 var btn = Roo.factory(b);
2211                 
2212                 btn.onRender(this.el.select('.modal-footer div').first());
2213                 
2214             },this);
2215         }
2216         // render the children.
2217         var nitems = [];
2218         
2219         if(typeof(this.items) != 'undefined'){
2220             var items = this.items;
2221             delete this.items;
2222
2223             for(var i =0;i < items.length;i++) {
2224                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2225             }
2226         }
2227         
2228         this.items = nitems;
2229         
2230         // where are these used - they used to be body/close/footer
2231         
2232        
2233         this.initEvents();
2234         //this.el.addClass([this.fieldClass, this.cls]);
2235         
2236     },
2237     getAutoCreate : function(){
2238         
2239         
2240         var bdy = {
2241                 cls : 'modal-body',
2242                 html : this.html || ''
2243         };
2244         
2245         var title = {
2246             tag: 'h4',
2247             cls : 'modal-title',
2248             html : this.title
2249         };
2250         
2251         if(this.specificTitle){
2252             title = this.title;
2253             
2254         };
2255         
2256         var header = [];
2257         if (this.allow_close) {
2258             header.push({
2259                 tag: 'button',
2260                 cls : 'close',
2261                 html : '&times'
2262             });
2263         }
2264         header.push(title);
2265         
2266         var modal = {
2267             cls: "modal",
2268             style : 'display: none',
2269             cn : [
2270                 {
2271                     cls: "modal-dialog",
2272                     cn : [
2273                         {
2274                             cls : "modal-content",
2275                             cn : [
2276                                 {
2277                                     cls : 'modal-header',
2278                                     cn : header
2279                                 },
2280                                 bdy,
2281                                 {
2282                                     cls : 'modal-footer',
2283                                     cn : [
2284                                         {
2285                                             tag: 'div',
2286                                             cls: 'btn-' + this.buttonPosition
2287                                         }
2288                                     ]
2289                                     
2290                                 }
2291                                 
2292                                 
2293                             ]
2294                             
2295                         }
2296                     ]
2297                         
2298                 }
2299             ]
2300         };
2301         
2302         if(this.animate){
2303             modal.cls += ' fade';
2304         }
2305         
2306         return modal;
2307           
2308     },
2309     getChildContainer : function() {
2310          
2311          return this.bodyEl;
2312         
2313     },
2314     getButtonContainer : function() {
2315          return this.el.select('.modal-footer div',true).first();
2316         
2317     },
2318     initEvents : function()
2319     {
2320         if (this.allow_close) {
2321             this.closeEl.on('click', this.hide, this);
2322         }
2323
2324     },
2325     show : function() {
2326         
2327         if (!this.rendered) {
2328             this.render();
2329         }
2330         
2331         this.el.setStyle('display', 'block');
2332         
2333         if(this.animate){
2334             var _this = this;
2335             (function(){ _this.el.addClass('in'); }).defer(50);
2336         }else{
2337             this.el.addClass('in');
2338         }
2339         
2340         // not sure how we can show data in here.. 
2341         //if (this.tmpl) {
2342         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2343         //}
2344         
2345         Roo.get(document.body).addClass("x-body-masked");
2346         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2347         this.maskEl.show();
2348         this.el.setStyle('zIndex', '10001');
2349        
2350         this.fireEvent('show', this);
2351         
2352         
2353     },
2354     hide : function()
2355     {
2356         this.maskEl.hide();
2357         Roo.get(document.body).removeClass("x-body-masked");
2358         this.el.removeClass('in');
2359         
2360         if(this.animate){
2361             var _this = this;
2362             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2363         }else{
2364             this.el.setStyle('display', 'none');
2365         }
2366         
2367         this.fireEvent('hide', this);
2368     },
2369     
2370     addButton : function(str, cb)
2371     {
2372          
2373         
2374         var b = Roo.apply({}, { html : str } );
2375         b.xns = b.xns || Roo.bootstrap;
2376         b.xtype = b.xtype || 'Button';
2377         if (typeof(b.listeners) == 'undefined') {
2378             b.listeners = { click : cb.createDelegate(this)  };
2379         }
2380         
2381         var btn = Roo.factory(b);
2382            
2383         btn.onRender(this.el.select('.modal-footer div').first());
2384         
2385         return btn;   
2386        
2387     },
2388     
2389     setDefaultButton : function(btn)
2390     {
2391         //this.el.select('.modal-footer').()
2392     },
2393     resizeTo: function(w,h)
2394     {
2395         // skip..
2396     },
2397     setContentSize  : function(w, h)
2398     {
2399         
2400     },
2401     onButtonClick: function(btn,e)
2402     {
2403         //Roo.log([a,b,c]);
2404         this.fireEvent('btnclick', btn.name, e);
2405     },
2406      /**
2407      * Set the title of the Dialog
2408      * @param {String} str new Title
2409      */
2410     setTitle: function(str) {
2411         this.titleEl.dom.innerHTML = str;    
2412     },
2413     /**
2414      * Set the body of the Dialog
2415      * @param {String} str new Title
2416      */
2417     setBody: function(str) {
2418         this.bodyEl.dom.innerHTML = str;    
2419     },
2420     /**
2421      * Set the body of the Dialog using the template
2422      * @param {Obj} data - apply this data to the template and replace the body contents.
2423      */
2424     applyBody: function(obj)
2425     {
2426         if (!this.tmpl) {
2427             Roo.log("Error - using apply Body without a template");
2428             //code
2429         }
2430         this.tmpl.overwrite(this.bodyEl, obj);
2431     }
2432     
2433 });
2434
2435
2436 Roo.apply(Roo.bootstrap.Modal,  {
2437     /**
2438          * Button config that displays a single OK button
2439          * @type Object
2440          */
2441         OK :  [{
2442             name : 'ok',
2443             weight : 'primary',
2444             html : 'OK'
2445         }], 
2446         /**
2447          * Button config that displays Yes and No buttons
2448          * @type Object
2449          */
2450         YESNO : [
2451             {
2452                 name  : 'no',
2453                 html : 'No'
2454             },
2455             {
2456                 name  :'yes',
2457                 weight : 'primary',
2458                 html : 'Yes'
2459             }
2460         ],
2461         
2462         /**
2463          * Button config that displays OK and Cancel buttons
2464          * @type Object
2465          */
2466         OKCANCEL : [
2467             {
2468                name : 'cancel',
2469                 html : 'Cancel'
2470             },
2471             {
2472                 name : 'ok',
2473                 weight : 'primary',
2474                 html : 'OK'
2475             }
2476         ],
2477         /**
2478          * Button config that displays Yes, No and Cancel buttons
2479          * @type Object
2480          */
2481         YESNOCANCEL : [
2482             {
2483                 name : 'yes',
2484                 weight : 'primary',
2485                 html : 'Yes'
2486             },
2487             {
2488                 name : 'no',
2489                 html : 'No'
2490             },
2491             {
2492                 name : 'cancel',
2493                 html : 'Cancel'
2494             }
2495         ]
2496 });
2497  
2498  /*
2499  * - LGPL
2500  *
2501  * messagebox - can be used as a replace
2502  * 
2503  */
2504 /**
2505  * @class Roo.MessageBox
2506  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2507  * Example usage:
2508  *<pre><code>
2509 // Basic alert:
2510 Roo.Msg.alert('Status', 'Changes saved successfully.');
2511
2512 // Prompt for user data:
2513 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2514     if (btn == 'ok'){
2515         // process text value...
2516     }
2517 });
2518
2519 // Show a dialog using config options:
2520 Roo.Msg.show({
2521    title:'Save Changes?',
2522    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2523    buttons: Roo.Msg.YESNOCANCEL,
2524    fn: processResult,
2525    animEl: 'elId'
2526 });
2527 </code></pre>
2528  * @singleton
2529  */
2530 Roo.bootstrap.MessageBox = function(){
2531     var dlg, opt, mask, waitTimer;
2532     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2533     var buttons, activeTextEl, bwidth;
2534
2535     
2536     // private
2537     var handleButton = function(button){
2538         dlg.hide();
2539         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2540     };
2541
2542     // private
2543     var handleHide = function(){
2544         if(opt && opt.cls){
2545             dlg.el.removeClass(opt.cls);
2546         }
2547         //if(waitTimer){
2548         //    Roo.TaskMgr.stop(waitTimer);
2549         //    waitTimer = null;
2550         //}
2551     };
2552
2553     // private
2554     var updateButtons = function(b){
2555         var width = 0;
2556         if(!b){
2557             buttons["ok"].hide();
2558             buttons["cancel"].hide();
2559             buttons["yes"].hide();
2560             buttons["no"].hide();
2561             //dlg.footer.dom.style.display = 'none';
2562             return width;
2563         }
2564         dlg.footerEl.dom.style.display = '';
2565         for(var k in buttons){
2566             if(typeof buttons[k] != "function"){
2567                 if(b[k]){
2568                     buttons[k].show();
2569                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2570                     width += buttons[k].el.getWidth()+15;
2571                 }else{
2572                     buttons[k].hide();
2573                 }
2574             }
2575         }
2576         return width;
2577     };
2578
2579     // private
2580     var handleEsc = function(d, k, e){
2581         if(opt && opt.closable !== false){
2582             dlg.hide();
2583         }
2584         if(e){
2585             e.stopEvent();
2586         }
2587     };
2588
2589     return {
2590         /**
2591          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2592          * @return {Roo.BasicDialog} The BasicDialog element
2593          */
2594         getDialog : function(){
2595            if(!dlg){
2596                 dlg = new Roo.bootstrap.Modal( {
2597                     //draggable: true,
2598                     //resizable:false,
2599                     //constraintoviewport:false,
2600                     //fixedcenter:true,
2601                     //collapsible : false,
2602                     //shim:true,
2603                     //modal: true,
2604                   //  width:400,
2605                   //  height:100,
2606                     //buttonAlign:"center",
2607                     closeClick : function(){
2608                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2609                             handleButton("no");
2610                         }else{
2611                             handleButton("cancel");
2612                         }
2613                     }
2614                 });
2615                 dlg.render();
2616                 dlg.on("hide", handleHide);
2617                 mask = dlg.mask;
2618                 //dlg.addKeyListener(27, handleEsc);
2619                 buttons = {};
2620                 this.buttons = buttons;
2621                 var bt = this.buttonText;
2622                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2623                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2624                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2625                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2626                 Roo.log(buttons)
2627                 bodyEl = dlg.bodyEl.createChild({
2628
2629                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2630                         '<textarea class="roo-mb-textarea"></textarea>' +
2631                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2632                 });
2633                 msgEl = bodyEl.dom.firstChild;
2634                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2635                 textboxEl.enableDisplayMode();
2636                 textboxEl.addKeyListener([10,13], function(){
2637                     if(dlg.isVisible() && opt && opt.buttons){
2638                         if(opt.buttons.ok){
2639                             handleButton("ok");
2640                         }else if(opt.buttons.yes){
2641                             handleButton("yes");
2642                         }
2643                     }
2644                 });
2645                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2646                 textareaEl.enableDisplayMode();
2647                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2648                 progressEl.enableDisplayMode();
2649                 var pf = progressEl.dom.firstChild;
2650                 if (pf) {
2651                     pp = Roo.get(pf.firstChild);
2652                     pp.setHeight(pf.offsetHeight);
2653                 }
2654                 
2655             }
2656             return dlg;
2657         },
2658
2659         /**
2660          * Updates the message box body text
2661          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2662          * the XHTML-compliant non-breaking space character '&amp;#160;')
2663          * @return {Roo.MessageBox} This message box
2664          */
2665         updateText : function(text){
2666             if(!dlg.isVisible() && !opt.width){
2667                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2668             }
2669             msgEl.innerHTML = text || '&#160;';
2670       
2671             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2672             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2673             var w = Math.max(
2674                     Math.min(opt.width || cw , this.maxWidth), 
2675                     Math.max(opt.minWidth || this.minWidth, bwidth)
2676             );
2677             if(opt.prompt){
2678                 activeTextEl.setWidth(w);
2679             }
2680             if(dlg.isVisible()){
2681                 dlg.fixedcenter = false;
2682             }
2683             // to big, make it scroll. = But as usual stupid IE does not support
2684             // !important..
2685             
2686             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2687                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2688                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2689             } else {
2690                 bodyEl.dom.style.height = '';
2691                 bodyEl.dom.style.overflowY = '';
2692             }
2693             if (cw > w) {
2694                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2695             } else {
2696                 bodyEl.dom.style.overflowX = '';
2697             }
2698             
2699             dlg.setContentSize(w, bodyEl.getHeight());
2700             if(dlg.isVisible()){
2701                 dlg.fixedcenter = true;
2702             }
2703             return this;
2704         },
2705
2706         /**
2707          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2708          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2709          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2710          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2711          * @return {Roo.MessageBox} This message box
2712          */
2713         updateProgress : function(value, text){
2714             if(text){
2715                 this.updateText(text);
2716             }
2717             if (pp) { // weird bug on my firefox - for some reason this is not defined
2718                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2719             }
2720             return this;
2721         },        
2722
2723         /**
2724          * Returns true if the message box is currently displayed
2725          * @return {Boolean} True if the message box is visible, else false
2726          */
2727         isVisible : function(){
2728             return dlg && dlg.isVisible();  
2729         },
2730
2731         /**
2732          * Hides the message box if it is displayed
2733          */
2734         hide : function(){
2735             if(this.isVisible()){
2736                 dlg.hide();
2737             }  
2738         },
2739
2740         /**
2741          * Displays a new message box, or reinitializes an existing message box, based on the config options
2742          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2743          * The following config object properties are supported:
2744          * <pre>
2745 Property    Type             Description
2746 ----------  ---------------  ------------------------------------------------------------------------------------
2747 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2748                                    closes (defaults to undefined)
2749 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2750                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2751 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2752                                    progress and wait dialogs will ignore this property and always hide the
2753                                    close button as they can only be closed programmatically.
2754 cls               String           A custom CSS class to apply to the message box element
2755 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2756                                    displayed (defaults to 75)
2757 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2758                                    function will be btn (the name of the button that was clicked, if applicable,
2759                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2760                                    Progress and wait dialogs will ignore this option since they do not respond to
2761                                    user actions and can only be closed programmatically, so any required function
2762                                    should be called by the same code after it closes the dialog.
2763 icon              String           A CSS class that provides a background image to be used as an icon for
2764                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2765 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2766 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2767 modal             Boolean          False to allow user interaction with the page while the message box is
2768                                    displayed (defaults to true)
2769 msg               String           A string that will replace the existing message box body text (defaults
2770                                    to the XHTML-compliant non-breaking space character '&#160;')
2771 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2772 progress          Boolean          True to display a progress bar (defaults to false)
2773 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2774 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2775 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2776 title             String           The title text
2777 value             String           The string value to set into the active textbox element if displayed
2778 wait              Boolean          True to display a progress bar (defaults to false)
2779 width             Number           The width of the dialog in pixels
2780 </pre>
2781          *
2782          * Example usage:
2783          * <pre><code>
2784 Roo.Msg.show({
2785    title: 'Address',
2786    msg: 'Please enter your address:',
2787    width: 300,
2788    buttons: Roo.MessageBox.OKCANCEL,
2789    multiline: true,
2790    fn: saveAddress,
2791    animEl: 'addAddressBtn'
2792 });
2793 </code></pre>
2794          * @param {Object} config Configuration options
2795          * @return {Roo.MessageBox} This message box
2796          */
2797         show : function(options)
2798         {
2799             
2800             // this causes nightmares if you show one dialog after another
2801             // especially on callbacks..
2802              
2803             if(this.isVisible()){
2804                 
2805                 this.hide();
2806                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2807                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2808                 Roo.log("New Dialog Message:" +  options.msg )
2809                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2810                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2811                 
2812             }
2813             var d = this.getDialog();
2814             opt = options;
2815             d.setTitle(opt.title || "&#160;");
2816             d.closeEl.setDisplayed(opt.closable !== false);
2817             activeTextEl = textboxEl;
2818             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2819             if(opt.prompt){
2820                 if(opt.multiline){
2821                     textboxEl.hide();
2822                     textareaEl.show();
2823                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2824                         opt.multiline : this.defaultTextHeight);
2825                     activeTextEl = textareaEl;
2826                 }else{
2827                     textboxEl.show();
2828                     textareaEl.hide();
2829                 }
2830             }else{
2831                 textboxEl.hide();
2832                 textareaEl.hide();
2833             }
2834             progressEl.setDisplayed(opt.progress === true);
2835             this.updateProgress(0);
2836             activeTextEl.dom.value = opt.value || "";
2837             if(opt.prompt){
2838                 dlg.setDefaultButton(activeTextEl);
2839             }else{
2840                 var bs = opt.buttons;
2841                 var db = null;
2842                 if(bs && bs.ok){
2843                     db = buttons["ok"];
2844                 }else if(bs && bs.yes){
2845                     db = buttons["yes"];
2846                 }
2847                 dlg.setDefaultButton(db);
2848             }
2849             bwidth = updateButtons(opt.buttons);
2850             this.updateText(opt.msg);
2851             if(opt.cls){
2852                 d.el.addClass(opt.cls);
2853             }
2854             d.proxyDrag = opt.proxyDrag === true;
2855             d.modal = opt.modal !== false;
2856             d.mask = opt.modal !== false ? mask : false;
2857             if(!d.isVisible()){
2858                 // force it to the end of the z-index stack so it gets a cursor in FF
2859                 document.body.appendChild(dlg.el.dom);
2860                 d.animateTarget = null;
2861                 d.show(options.animEl);
2862             }
2863             return this;
2864         },
2865
2866         /**
2867          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2868          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2869          * and closing the message box when the process is complete.
2870          * @param {String} title The title bar text
2871          * @param {String} msg The message box body text
2872          * @return {Roo.MessageBox} This message box
2873          */
2874         progress : function(title, msg){
2875             this.show({
2876                 title : title,
2877                 msg : msg,
2878                 buttons: false,
2879                 progress:true,
2880                 closable:false,
2881                 minWidth: this.minProgressWidth,
2882                 modal : true
2883             });
2884             return this;
2885         },
2886
2887         /**
2888          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2889          * If a callback function is passed it will be called after the user clicks the button, and the
2890          * id of the button that was clicked will be passed as the only parameter to the callback
2891          * (could also be the top-right close button).
2892          * @param {String} title The title bar text
2893          * @param {String} msg The message box body text
2894          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2895          * @param {Object} scope (optional) The scope of the callback function
2896          * @return {Roo.MessageBox} This message box
2897          */
2898         alert : function(title, msg, fn, scope){
2899             this.show({
2900                 title : title,
2901                 msg : msg,
2902                 buttons: this.OK,
2903                 fn: fn,
2904                 scope : scope,
2905                 modal : true
2906             });
2907             return this;
2908         },
2909
2910         /**
2911          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2912          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2913          * You are responsible for closing the message box when the process is complete.
2914          * @param {String} msg The message box body text
2915          * @param {String} title (optional) The title bar text
2916          * @return {Roo.MessageBox} This message box
2917          */
2918         wait : function(msg, title){
2919             this.show({
2920                 title : title,
2921                 msg : msg,
2922                 buttons: false,
2923                 closable:false,
2924                 progress:true,
2925                 modal:true,
2926                 width:300,
2927                 wait:true
2928             });
2929             waitTimer = Roo.TaskMgr.start({
2930                 run: function(i){
2931                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2932                 },
2933                 interval: 1000
2934             });
2935             return this;
2936         },
2937
2938         /**
2939          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2940          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2941          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2942          * @param {String} title The title bar text
2943          * @param {String} msg The message box body text
2944          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2945          * @param {Object} scope (optional) The scope of the callback function
2946          * @return {Roo.MessageBox} This message box
2947          */
2948         confirm : function(title, msg, fn, scope){
2949             this.show({
2950                 title : title,
2951                 msg : msg,
2952                 buttons: this.YESNO,
2953                 fn: fn,
2954                 scope : scope,
2955                 modal : true
2956             });
2957             return this;
2958         },
2959
2960         /**
2961          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2962          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2963          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2964          * (could also be the top-right close button) and the text that was entered will be passed as the two
2965          * parameters to the callback.
2966          * @param {String} title The title bar text
2967          * @param {String} msg The message box body text
2968          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2969          * @param {Object} scope (optional) The scope of the callback function
2970          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2971          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2972          * @return {Roo.MessageBox} This message box
2973          */
2974         prompt : function(title, msg, fn, scope, multiline){
2975             this.show({
2976                 title : title,
2977                 msg : msg,
2978                 buttons: this.OKCANCEL,
2979                 fn: fn,
2980                 minWidth:250,
2981                 scope : scope,
2982                 prompt:true,
2983                 multiline: multiline,
2984                 modal : true
2985             });
2986             return this;
2987         },
2988
2989         /**
2990          * Button config that displays a single OK button
2991          * @type Object
2992          */
2993         OK : {ok:true},
2994         /**
2995          * Button config that displays Yes and No buttons
2996          * @type Object
2997          */
2998         YESNO : {yes:true, no:true},
2999         /**
3000          * Button config that displays OK and Cancel buttons
3001          * @type Object
3002          */
3003         OKCANCEL : {ok:true, cancel:true},
3004         /**
3005          * Button config that displays Yes, No and Cancel buttons
3006          * @type Object
3007          */
3008         YESNOCANCEL : {yes:true, no:true, cancel:true},
3009
3010         /**
3011          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3012          * @type Number
3013          */
3014         defaultTextHeight : 75,
3015         /**
3016          * The maximum width in pixels of the message box (defaults to 600)
3017          * @type Number
3018          */
3019         maxWidth : 600,
3020         /**
3021          * The minimum width in pixels of the message box (defaults to 100)
3022          * @type Number
3023          */
3024         minWidth : 100,
3025         /**
3026          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3027          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3028          * @type Number
3029          */
3030         minProgressWidth : 250,
3031         /**
3032          * An object containing the default button text strings that can be overriden for localized language support.
3033          * Supported properties are: ok, cancel, yes and no.
3034          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3035          * @type Object
3036          */
3037         buttonText : {
3038             ok : "OK",
3039             cancel : "Cancel",
3040             yes : "Yes",
3041             no : "No"
3042         }
3043     };
3044 }();
3045
3046 /**
3047  * Shorthand for {@link Roo.MessageBox}
3048  */
3049 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3050 Roo.Msg = Roo.Msg || Roo.MessageBox;
3051 /*
3052  * - LGPL
3053  *
3054  * navbar
3055  * 
3056  */
3057
3058 /**
3059  * @class Roo.bootstrap.Navbar
3060  * @extends Roo.bootstrap.Component
3061  * Bootstrap Navbar class
3062
3063  * @constructor
3064  * Create a new Navbar
3065  * @param {Object} config The config object
3066  */
3067
3068
3069 Roo.bootstrap.Navbar = function(config){
3070     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3071     
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3075     
3076     
3077    
3078     // private
3079     navItems : false,
3080     loadMask : false,
3081     
3082     
3083     getAutoCreate : function(){
3084         
3085         
3086         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3087         
3088     },
3089     
3090     initEvents :function ()
3091     {
3092         //Roo.log(this.el.select('.navbar-toggle',true));
3093         this.el.select('.navbar-toggle',true).on('click', function() {
3094            // Roo.log('click');
3095             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3096         }, this);
3097         
3098         var mark = {
3099             tag: "div",
3100             cls:"x-dlg-mask"
3101         }
3102         
3103         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3104         
3105         var size = this.el.getSize();
3106         this.maskEl.setSize(size.width, size.height);
3107         this.maskEl.enableDisplayMode("block");
3108         this.maskEl.hide();
3109         
3110         if(this.loadMask){
3111             this.maskEl.show();
3112         }
3113     },
3114     
3115     
3116     getChildContainer : function()
3117     {
3118         if (this.el.select('.collapse').getCount()) {
3119             return this.el.select('.collapse',true).first();
3120         }
3121         
3122         return this.el;
3123     },
3124     
3125     mask : function()
3126     {
3127         this.maskEl.show();
3128     },
3129     
3130     unmask : function()
3131     {
3132         this.maskEl.hide();
3133     } 
3134     
3135     
3136     
3137     
3138 });
3139
3140
3141
3142  
3143
3144  /*
3145  * - LGPL
3146  *
3147  * navbar
3148  * 
3149  */
3150
3151 /**
3152  * @class Roo.bootstrap.NavSimplebar
3153  * @extends Roo.bootstrap.Navbar
3154  * Bootstrap Sidebar class
3155  *
3156  * @cfg {Boolean} inverse is inverted color
3157  * 
3158  * @cfg {String} type (nav | pills | tabs)
3159  * @cfg {Boolean} arrangement stacked | justified
3160  * @cfg {String} align (left | right) alignment
3161  * 
3162  * @cfg {Boolean} main (true|false) main nav bar? default false
3163  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3164  * 
3165  * @cfg {String} tag (header|footer|nav|div) default is nav 
3166
3167  * 
3168  * 
3169  * 
3170  * @constructor
3171  * Create a new Sidebar
3172  * @param {Object} config The config object
3173  */
3174
3175
3176 Roo.bootstrap.NavSimplebar = function(config){
3177     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3178 };
3179
3180 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3181     
3182     inverse: false,
3183     
3184     type: false,
3185     arrangement: '',
3186     align : false,
3187     
3188     
3189     
3190     main : false,
3191     
3192     
3193     tag : false,
3194     
3195     
3196     getAutoCreate : function(){
3197         
3198         
3199         var cfg = {
3200             tag : this.tag || 'div',
3201             cls : 'navbar'
3202         };
3203           
3204         
3205         cfg.cn = [
3206             {
3207                 cls: 'nav',
3208                 tag : 'ul'
3209             }
3210         ];
3211         
3212          
3213         this.type = this.type || 'nav';
3214         if (['tabs','pills'].indexOf(this.type)!==-1) {
3215             cfg.cn[0].cls += ' nav-' + this.type
3216         
3217         
3218         } else {
3219             if (this.type!=='nav') {
3220                 Roo.log('nav type must be nav/tabs/pills')
3221             }
3222             cfg.cn[0].cls += ' navbar-nav'
3223         }
3224         
3225         
3226         
3227         
3228         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3229             cfg.cn[0].cls += ' nav-' + this.arrangement;
3230         }
3231         
3232         
3233         if (this.align === 'right') {
3234             cfg.cn[0].cls += ' navbar-right';
3235         }
3236         
3237         if (this.inverse) {
3238             cfg.cls += ' navbar-inverse';
3239             
3240         }
3241         
3242         
3243         return cfg;
3244     
3245         
3246     }
3247     
3248     
3249     
3250 });
3251
3252
3253
3254  
3255
3256  
3257        /*
3258  * - LGPL
3259  *
3260  * navbar
3261  * 
3262  */
3263
3264 /**
3265  * @class Roo.bootstrap.NavHeaderbar
3266  * @extends Roo.bootstrap.NavSimplebar
3267  * Bootstrap Sidebar class
3268  *
3269  * @cfg {String} brand what is brand
3270  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3271  * @cfg {String} brand_href href of the brand
3272  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3273  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3274  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3275  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3276  * 
3277  * @constructor
3278  * Create a new Sidebar
3279  * @param {Object} config The config object
3280  */
3281
3282
3283 Roo.bootstrap.NavHeaderbar = function(config){
3284     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3285       
3286 };
3287
3288 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3289     
3290     position: '',
3291     brand: '',
3292     brand_href: false,
3293     srButton : true,
3294     autohide : false,
3295     desktopCenter : false,
3296    
3297     
3298     getAutoCreate : function(){
3299         
3300         var   cfg = {
3301             tag: this.nav || 'nav',
3302             cls: 'navbar',
3303             role: 'navigation',
3304             cn: []
3305         };
3306         
3307         var cn = cfg.cn;
3308         if (this.desktopCenter) {
3309             cn.push({cls : 'container', cn : []});
3310             cn = cn[0].cn;
3311         }
3312         
3313         if(this.srButton){
3314             cn.push({
3315                 tag: 'div',
3316                 cls: 'navbar-header',
3317                 cn: [
3318                     {
3319                         tag: 'button',
3320                         type: 'button',
3321                         cls: 'navbar-toggle',
3322                         'data-toggle': 'collapse',
3323                         cn: [
3324                             {
3325                                 tag: 'span',
3326                                 cls: 'sr-only',
3327                                 html: 'Toggle navigation'
3328                             },
3329                             {
3330                                 tag: 'span',
3331                                 cls: 'icon-bar'
3332                             },
3333                             {
3334                                 tag: 'span',
3335                                 cls: 'icon-bar'
3336                             },
3337                             {
3338                                 tag: 'span',
3339                                 cls: 'icon-bar'
3340                             }
3341                         ]
3342                     }
3343                 ]
3344             });
3345         }
3346         
3347         cn.push({
3348             tag: 'div',
3349             cls: 'collapse navbar-collapse',
3350             cn : []
3351         });
3352         
3353         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3354         
3355         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3356             cfg.cls += ' navbar-' + this.position;
3357             
3358             // tag can override this..
3359             
3360             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3361         }
3362         
3363         if (this.brand !== '') {
3364             cn[0].cn.push({
3365                 tag: 'a',
3366                 href: this.brand_href ? this.brand_href : '#',
3367                 cls: 'navbar-brand',
3368                 cn: [
3369                 this.brand
3370                 ]
3371             });
3372         }
3373         
3374         if(this.main){
3375             cfg.cls += ' main-nav';
3376         }
3377         
3378         
3379         return cfg;
3380
3381         
3382     },
3383     getHeaderChildContainer : function()
3384     {
3385         if (this.el.select('.navbar-header').getCount()) {
3386             return this.el.select('.navbar-header',true).first();
3387         }
3388         
3389         return this.getChildContainer();
3390     },
3391     
3392     
3393     initEvents : function()
3394     {
3395         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3396         
3397         if (this.autohide) {
3398             
3399             var prevScroll = 0;
3400             var ft = this.el;
3401             
3402             Roo.get(document).on('scroll',function(e) {
3403                 var ns = Roo.get(document).getScroll().top;
3404                 var os = prevScroll;
3405                 prevScroll = ns;
3406                 
3407                 if(ns > os){
3408                     ft.removeClass('slideDown');
3409                     ft.addClass('slideUp');
3410                     return;
3411                 }
3412                 ft.removeClass('slideUp');
3413                 ft.addClass('slideDown');
3414                  
3415               
3416           },this);
3417         }
3418     }    
3419     
3420 });
3421
3422
3423
3424  
3425
3426  /*
3427  * - LGPL
3428  *
3429  * navbar
3430  * 
3431  */
3432
3433 /**
3434  * @class Roo.bootstrap.NavSidebar
3435  * @extends Roo.bootstrap.Navbar
3436  * Bootstrap Sidebar class
3437  * 
3438  * @constructor
3439  * Create a new Sidebar
3440  * @param {Object} config The config object
3441  */
3442
3443
3444 Roo.bootstrap.NavSidebar = function(config){
3445     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3446 };
3447
3448 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3449     
3450     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3451     
3452     getAutoCreate : function(){
3453         
3454         
3455         return  {
3456             tag: 'div',
3457             cls: 'sidebar sidebar-nav'
3458         };
3459     
3460         
3461     }
3462     
3463     
3464     
3465 });
3466
3467
3468
3469  
3470
3471  /*
3472  * - LGPL
3473  *
3474  * nav group
3475  * 
3476  */
3477
3478 /**
3479  * @class Roo.bootstrap.NavGroup
3480  * @extends Roo.bootstrap.Component
3481  * Bootstrap NavGroup class
3482  * @cfg {String} align left | right
3483  * @cfg {Boolean} inverse false | true
3484  * @cfg {String} type (nav|pills|tab) default nav
3485  * @cfg {String} navId - reference Id for navbar.
3486
3487  * 
3488  * @constructor
3489  * Create a new nav group
3490  * @param {Object} config The config object
3491  */
3492
3493 Roo.bootstrap.NavGroup = function(config){
3494     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3495     this.navItems = [];
3496    
3497     Roo.bootstrap.NavGroup.register(this);
3498      this.addEvents({
3499         /**
3500              * @event changed
3501              * Fires when the active item changes
3502              * @param {Roo.bootstrap.NavGroup} this
3503              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3504              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3505          */
3506         'changed': true
3507      });
3508     
3509 };
3510
3511 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3512     
3513     align: '',
3514     inverse: false,
3515     form: false,
3516     type: 'nav',
3517     navId : '',
3518     // private
3519     
3520     navItems : false, 
3521     
3522     getAutoCreate : function()
3523     {
3524         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3525         
3526         cfg = {
3527             tag : 'ul',
3528             cls: 'nav' 
3529         }
3530         
3531         if (['tabs','pills'].indexOf(this.type)!==-1) {
3532             cfg.cls += ' nav-' + this.type
3533         } else {
3534             if (this.type!=='nav') {
3535                 Roo.log('nav type must be nav/tabs/pills')
3536             }
3537             cfg.cls += ' navbar-nav'
3538         }
3539         
3540         if (this.parent().sidebar) {
3541             cfg = {
3542                 tag: 'ul',
3543                 cls: 'dashboard-menu sidebar-menu'
3544             }
3545             
3546             return cfg;
3547         }
3548         
3549         if (this.form === true) {
3550             cfg = {
3551                 tag: 'form',
3552                 cls: 'navbar-form'
3553             }
3554             
3555             if (this.align === 'right') {
3556                 cfg.cls += ' navbar-right';
3557             } else {
3558                 cfg.cls += ' navbar-left';
3559             }
3560         }
3561         
3562         if (this.align === 'right') {
3563             cfg.cls += ' navbar-right';
3564         }
3565         
3566         if (this.inverse) {
3567             cfg.cls += ' navbar-inverse';
3568             
3569         }
3570         
3571         
3572         return cfg;
3573     },
3574     /**
3575     * sets the active Navigation item
3576     * @param {Roo.bootstrap.NavItem} the new current navitem
3577     */
3578     setActiveItem : function(item)
3579     {
3580         var prev = false;
3581         Roo.each(this.navItems, function(v){
3582             if (v == item) {
3583                 return ;
3584             }
3585             if (v.isActive()) {
3586                 v.setActive(false, true);
3587                 prev = v;
3588                 
3589             }
3590             
3591         });
3592
3593         item.setActive(true, true);
3594         this.fireEvent('changed', this, item, prev);
3595         
3596         
3597     },
3598     /**
3599     * gets the active Navigation item
3600     * @return {Roo.bootstrap.NavItem} the current navitem
3601     */
3602     getActive : function()
3603     {
3604         
3605         var prev = false;
3606         Roo.each(this.navItems, function(v){
3607             
3608             if (v.isActive()) {
3609                 prev = v;
3610                 
3611             }
3612             
3613         });
3614         return prev;
3615     },
3616     
3617     indexOfNav : function()
3618     {
3619         
3620         var prev = false;
3621         Roo.each(this.navItems, function(v,i){
3622             
3623             if (v.isActive()) {
3624                 prev = i;
3625                 
3626             }
3627             
3628         });
3629         return prev;
3630     },
3631     /**
3632     * adds a Navigation item
3633     * @param {Roo.bootstrap.NavItem} the navitem to add
3634     */
3635     addItem : function(cfg)
3636     {
3637         var cn = new Roo.bootstrap.NavItem(cfg);
3638         this.register(cn);
3639         cn.parentId = this.id;
3640         cn.onRender(this.el, null);
3641         return cn;
3642     },
3643     /**
3644     * register a Navigation item
3645     * @param {Roo.bootstrap.NavItem} the navitem to add
3646     */
3647     register : function(item)
3648     {
3649         this.navItems.push( item);
3650         item.navId = this.navId;
3651     
3652     },
3653     
3654     /**
3655     * clear all the Navigation item
3656     */
3657    
3658     clearAll : function()
3659     {
3660         this.navItems = [];
3661         this.el.dom.innerHTML = '';
3662     },
3663     
3664     getNavItem: function(tabId)
3665     {
3666         var ret = false;
3667         Roo.each(this.navItems, function(e) {
3668             if (e.tabId == tabId) {
3669                ret =  e;
3670                return false;
3671             }
3672             return true;
3673             
3674         });
3675         return ret;
3676     },
3677     
3678     setActiveNext : function()
3679     {
3680         var i = this.indexOfNav(this.getActive());
3681         if (i > this.navItems.length) {
3682             return;
3683         }
3684         this.setActiveItem(this.navItems[i+1]);
3685     },
3686     setActivePrev : function()
3687     {
3688         var i = this.indexOfNav(this.getActive());
3689         if (i  < 1) {
3690             return;
3691         }
3692         this.setActiveItem(this.navItems[i-1]);
3693     },
3694     clearWasActive : function(except) {
3695         Roo.each(this.navItems, function(e) {
3696             if (e.tabId != except.tabId && e.was_active) {
3697                e.was_active = false;
3698                return false;
3699             }
3700             return true;
3701             
3702         });
3703     },
3704     getWasActive : function ()
3705     {
3706         var r = false;
3707         Roo.each(this.navItems, function(e) {
3708             if (e.was_active) {
3709                r = e;
3710                return false;
3711             }
3712             return true;
3713             
3714         });
3715         return r;
3716     }
3717     
3718     
3719 });
3720
3721  
3722 Roo.apply(Roo.bootstrap.NavGroup, {
3723     
3724     groups: {},
3725      /**
3726     * register a Navigation Group
3727     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3728     */
3729     register : function(navgrp)
3730     {
3731         this.groups[navgrp.navId] = navgrp;
3732         
3733     },
3734     /**
3735     * fetch a Navigation Group based on the navigation ID
3736     * @param {string} the navgroup to add
3737     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3738     */
3739     get: function(navId) {
3740         if (typeof(this.groups[navId]) == 'undefined') {
3741             return false;
3742             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3743         }
3744         return this.groups[navId] ;
3745     }
3746     
3747     
3748     
3749 });
3750
3751  /*
3752  * - LGPL
3753  *
3754  * row
3755  * 
3756  */
3757
3758 /**
3759  * @class Roo.bootstrap.NavItem
3760  * @extends Roo.bootstrap.Component
3761  * Bootstrap Navbar.NavItem class
3762  * @cfg {String} href  link to
3763  * @cfg {String} html content of button
3764  * @cfg {String} badge text inside badge
3765  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3766  * @cfg {String} glyphicon name of glyphicon
3767  * @cfg {String} icon name of font awesome icon
3768  * @cfg {Boolean} active Is item active
3769  * @cfg {Boolean} disabled Is item disabled
3770  
3771  * @cfg {Boolean} preventDefault (true | false) default false
3772  * @cfg {String} tabId the tab that this item activates.
3773  * @cfg {String} tagtype (a|span) render as a href or span?
3774  * @cfg {Boolean} animateRef (true|false) link to element default false  
3775   
3776  * @constructor
3777  * Create a new Navbar Item
3778  * @param {Object} config The config object
3779  */
3780 Roo.bootstrap.NavItem = function(config){
3781     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3782     this.addEvents({
3783         // raw events
3784         /**
3785          * @event click
3786          * The raw click event for the entire grid.
3787          * @param {Roo.EventObject} e
3788          */
3789         "click" : true,
3790          /**
3791             * @event changed
3792             * Fires when the active item active state changes
3793             * @param {Roo.bootstrap.NavItem} this
3794             * @param {boolean} state the new state
3795              
3796          */
3797         'changed': true,
3798         /**
3799             * @event scrollto
3800             * Fires when scroll to element
3801             * @param {Roo.bootstrap.NavItem} this
3802             * @param {Object} options
3803             * @param {Roo.EventObject} e
3804              
3805          */
3806         'scrollto': true
3807     });
3808    
3809 };
3810
3811 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3812     
3813     href: false,
3814     html: '',
3815     badge: '',
3816     icon: false,
3817     glyphicon: false,
3818     active: false,
3819     preventDefault : false,
3820     tabId : false,
3821     tagtype : 'a',
3822     disabled : false,
3823     animateRef : false,
3824     was_active : false,
3825     
3826     getAutoCreate : function(){
3827          
3828         var cfg = {
3829             tag: 'li',
3830             cls: 'nav-item'
3831             
3832         }
3833         if (this.active) {
3834             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3835         }
3836         if (this.disabled) {
3837             cfg.cls += ' disabled';
3838         }
3839         
3840         if (this.href || this.html || this.glyphicon || this.icon) {
3841             cfg.cn = [
3842                 {
3843                     tag: this.tagtype,
3844                     href : this.href || "#",
3845                     html: this.html || ''
3846                 }
3847             ];
3848             
3849             if (this.icon) {
3850                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3851             }
3852
3853             if(this.glyphicon) {
3854                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3855             }
3856             
3857             if (this.menu) {
3858                 
3859                 cfg.cn[0].html += " <span class='caret'></span>";
3860              
3861             }
3862             
3863             if (this.badge !== '') {
3864                  
3865                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3866             }
3867         }
3868         
3869         
3870         
3871         return cfg;
3872     },
3873     initEvents: function() 
3874     {
3875         if (typeof (this.menu) != 'undefined') {
3876             this.menu.parentType = this.xtype;
3877             this.menu.triggerEl = this.el;
3878             this.menu = this.addxtype(Roo.apply({}, this.menu));
3879         }
3880         
3881         this.el.select('a',true).on('click', this.onClick, this);
3882         
3883         if(this.tagtype == 'span'){
3884             this.el.select('span',true).on('click', this.onClick, this);
3885         }
3886        
3887         // at this point parent should be available..
3888         this.parent().register(this);
3889     },
3890     
3891     onClick : function(e)
3892     {
3893         if(
3894                 this.preventDefault || 
3895                 this.href == '#' 
3896         ){
3897             
3898             e.preventDefault();
3899         }
3900         
3901         if (this.disabled) {
3902             return;
3903         }
3904         
3905         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3906         if (tg && tg.transition) {
3907             Roo.log("waiting for the transitionend");
3908             return;
3909         }
3910         
3911         
3912         
3913         //Roo.log("fire event clicked");
3914         if(this.fireEvent('click', this, e) === false){
3915             return;
3916         };
3917         
3918         if(this.tagtype == 'span'){
3919             return;
3920         }
3921         
3922         //Roo.log(this.href);
3923         var ael = this.el.select('a',true).first();
3924         //Roo.log(ael);
3925         
3926         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3927             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3928             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3929                 return; // ignore... - it's a 'hash' to another page.
3930             }
3931             
3932             e.preventDefault();
3933             this.scrollToElement(e);
3934         }
3935         
3936         
3937         var p =  this.parent();
3938    
3939         if (['tabs','pills'].indexOf(p.type)!==-1) {
3940             if (typeof(p.setActiveItem) !== 'undefined') {
3941                 p.setActiveItem(this);
3942             }
3943         }
3944     },
3945     
3946     isActive: function () {
3947         return this.active
3948     },
3949     setActive : function(state, fire, is_was_active)
3950     {
3951         if (this.active && !state & this.navId) {
3952             this.was_active = true;
3953             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3954             if (nv) {
3955                 nv.clearWasActive(this);
3956             }
3957             
3958         }
3959         this.active = state;
3960         
3961         if (!state ) {
3962             this.el.removeClass('active');
3963         } else if (!this.el.hasClass('active')) {
3964             this.el.addClass('active');
3965         }
3966         if (fire) {
3967             this.fireEvent('changed', this, state);
3968         }
3969         
3970         // show a panel if it's registered and related..
3971         
3972         if (!this.navId || !this.tabId || !state || is_was_active) {
3973             return;
3974         }
3975         
3976         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3977         if (!tg) {
3978             return;
3979         }
3980         var pan = tg.getPanelByName(this.tabId);
3981         if (!pan) {
3982             return;
3983         }
3984         // if we can not flip to new panel - go back to old nav highlight..
3985         if (false == tg.showPanel(pan)) {
3986             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3987             if (nv) {
3988                 var onav = nv.getWasActive();
3989                 if (onav) {
3990                     onav.setActive(true, false, true);
3991                 }
3992             }
3993             
3994         }
3995         
3996         
3997         
3998     },
3999      // this should not be here...
4000     setDisabled : function(state)
4001     {
4002         this.disabled = state;
4003         if (!state ) {
4004             this.el.removeClass('disabled');
4005         } else if (!this.el.hasClass('disabled')) {
4006             this.el.addClass('disabled');
4007         }
4008         
4009     },
4010     
4011     /**
4012      * Fetch the element to display the tooltip on.
4013      * @return {Roo.Element} defaults to this.el
4014      */
4015     tooltipEl : function()
4016     {
4017         return this.el.select('' + this.tagtype + '', true).first();
4018     },
4019     
4020     scrollToElement : function(e)
4021     {
4022         var c = document.body;
4023         
4024         /*
4025          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4026          */
4027         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4028             c = document.documentElement;
4029         }
4030         
4031         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4032         
4033         if(!target){
4034             return;
4035         }
4036
4037         var o = target.calcOffsetsTo(c);
4038         
4039         var options = {
4040             target : target,
4041             value : o[1]
4042         }
4043         
4044         this.fireEvent('scrollto', this, options, e);
4045         
4046         Roo.get(c).scrollTo('top', options.value, true);
4047         
4048         return;
4049     }
4050 });
4051  
4052
4053  /*
4054  * - LGPL
4055  *
4056  * sidebar item
4057  *
4058  *  li
4059  *    <span> icon </span>
4060  *    <span> text </span>
4061  *    <span>badge </span>
4062  */
4063
4064 /**
4065  * @class Roo.bootstrap.NavSidebarItem
4066  * @extends Roo.bootstrap.NavItem
4067  * Bootstrap Navbar.NavSidebarItem class
4068  * @constructor
4069  * Create a new Navbar Button
4070  * @param {Object} config The config object
4071  */
4072 Roo.bootstrap.NavSidebarItem = function(config){
4073     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4074     this.addEvents({
4075         // raw events
4076         /**
4077          * @event click
4078          * The raw click event for the entire grid.
4079          * @param {Roo.EventObject} e
4080          */
4081         "click" : true,
4082          /**
4083             * @event changed
4084             * Fires when the active item active state changes
4085             * @param {Roo.bootstrap.NavSidebarItem} this
4086             * @param {boolean} state the new state
4087              
4088          */
4089         'changed': true
4090     });
4091    
4092 };
4093
4094 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4095     
4096     
4097     getAutoCreate : function(){
4098         
4099         
4100         var a = {
4101                 tag: 'a',
4102                 href : this.href || '#',
4103                 cls: '',
4104                 html : '',
4105                 cn : []
4106         };
4107         var cfg = {
4108             tag: 'li',
4109             cls: '',
4110             cn: [ a ]
4111         }
4112         var span = {
4113             tag: 'span',
4114             html : this.html || ''
4115         }
4116         
4117         
4118         if (this.active) {
4119             cfg.cls += ' active';
4120         }
4121         
4122         // left icon..
4123         if (this.glyphicon || this.icon) {
4124             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4125             a.cn.push({ tag : 'i', cls : c }) ;
4126         }
4127         // html..
4128         a.cn.push(span);
4129         // then badge..
4130         if (this.badge !== '') {
4131             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4132         }
4133         // fi
4134         if (this.menu) {
4135             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4136             a.cls += 'dropdown-toggle treeview' ;
4137             
4138         }
4139         
4140         
4141         
4142         return cfg;
4143          
4144            
4145     }
4146    
4147      
4148  
4149 });
4150  
4151
4152  /*
4153  * - LGPL
4154  *
4155  * row
4156  * 
4157  */
4158
4159 /**
4160  * @class Roo.bootstrap.Row
4161  * @extends Roo.bootstrap.Component
4162  * Bootstrap Row class (contains columns...)
4163  * 
4164  * @constructor
4165  * Create a new Row
4166  * @param {Object} config The config object
4167  */
4168
4169 Roo.bootstrap.Row = function(config){
4170     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4171 };
4172
4173 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4174     
4175     getAutoCreate : function(){
4176        return {
4177             cls: 'row clearfix'
4178        };
4179     }
4180     
4181     
4182 });
4183
4184  
4185
4186  /*
4187  * - LGPL
4188  *
4189  * element
4190  * 
4191  */
4192
4193 /**
4194  * @class Roo.bootstrap.Element
4195  * @extends Roo.bootstrap.Component
4196  * Bootstrap Element class
4197  * @cfg {String} html contents of the element
4198  * @cfg {String} tag tag of the element
4199  * @cfg {String} cls class of the element
4200  * @cfg {Boolean} preventDefault (true|false) default false
4201  * @cfg {Boolean} clickable (true|false) default false
4202  * 
4203  * @constructor
4204  * Create a new Element
4205  * @param {Object} config The config object
4206  */
4207
4208 Roo.bootstrap.Element = function(config){
4209     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4210     
4211     this.addEvents({
4212         // raw events
4213         /**
4214          * @event click
4215          * When a element is chick
4216          * @param {Roo.bootstrap.Element} this
4217          * @param {Roo.EventObject} e
4218          */
4219         "click" : true
4220     });
4221 };
4222
4223 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4224     
4225     tag: 'div',
4226     cls: '',
4227     html: '',
4228     preventDefault: false, 
4229     clickable: false,
4230     
4231     getAutoCreate : function(){
4232         
4233         var cfg = {
4234             tag: this.tag,
4235             cls: this.cls,
4236             html: this.html
4237         }
4238         
4239         return cfg;
4240     },
4241     
4242     initEvents: function() 
4243     {
4244         Roo.bootstrap.Element.superclass.initEvents.call(this);
4245         
4246         if(this.clickable){
4247             this.el.on('click', this.onClick, this);
4248         }
4249         
4250     },
4251     
4252     onClick : function(e)
4253     {
4254         if(this.preventDefault){
4255             e.preventDefault();
4256         }
4257         
4258         this.fireEvent('click', this, e);
4259     },
4260     
4261     getValue : function()
4262     {
4263         return this.el.dom.innerHTML;
4264     },
4265     
4266     setValue : function(value)
4267     {
4268         this.el.dom.innerHTML = value;
4269     }
4270    
4271 });
4272
4273  
4274
4275  /*
4276  * - LGPL
4277  *
4278  * pagination
4279  * 
4280  */
4281
4282 /**
4283  * @class Roo.bootstrap.Pagination
4284  * @extends Roo.bootstrap.Component
4285  * Bootstrap Pagination class
4286  * @cfg {String} size xs | sm | md | lg
4287  * @cfg {Boolean} inverse false | true
4288  * 
4289  * @constructor
4290  * Create a new Pagination
4291  * @param {Object} config The config object
4292  */
4293
4294 Roo.bootstrap.Pagination = function(config){
4295     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4296 };
4297
4298 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4299     
4300     cls: false,
4301     size: false,
4302     inverse: false,
4303     
4304     getAutoCreate : function(){
4305         var cfg = {
4306             tag: 'ul',
4307                 cls: 'pagination'
4308         };
4309         if (this.inverse) {
4310             cfg.cls += ' inverse';
4311         }
4312         if (this.html) {
4313             cfg.html=this.html;
4314         }
4315         if (this.cls) {
4316             cfg.cls += " " + this.cls;
4317         }
4318         return cfg;
4319     }
4320    
4321 });
4322
4323  
4324
4325  /*
4326  * - LGPL
4327  *
4328  * Pagination item
4329  * 
4330  */
4331
4332
4333 /**
4334  * @class Roo.bootstrap.PaginationItem
4335  * @extends Roo.bootstrap.Component
4336  * Bootstrap PaginationItem class
4337  * @cfg {String} html text
4338  * @cfg {String} href the link
4339  * @cfg {Boolean} preventDefault (true | false) default true
4340  * @cfg {Boolean} active (true | false) default false
4341  * @cfg {Boolean} disabled default false
4342  * 
4343  * 
4344  * @constructor
4345  * Create a new PaginationItem
4346  * @param {Object} config The config object
4347  */
4348
4349
4350 Roo.bootstrap.PaginationItem = function(config){
4351     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4352     this.addEvents({
4353         // raw events
4354         /**
4355          * @event click
4356          * The raw click event for the entire grid.
4357          * @param {Roo.EventObject} e
4358          */
4359         "click" : true
4360     });
4361 };
4362
4363 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4364     
4365     href : false,
4366     html : false,
4367     preventDefault: true,
4368     active : false,
4369     cls : false,
4370     disabled: false,
4371     
4372     getAutoCreate : function(){
4373         var cfg= {
4374             tag: 'li',
4375             cn: [
4376                 {
4377                     tag : 'a',
4378                     href : this.href ? this.href : '#',
4379                     html : this.html ? this.html : ''
4380                 }
4381             ]
4382         };
4383         
4384         if(this.cls){
4385             cfg.cls = this.cls;
4386         }
4387         
4388         if(this.disabled){
4389             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4390         }
4391         
4392         if(this.active){
4393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4394         }
4395         
4396         return cfg;
4397     },
4398     
4399     initEvents: function() {
4400         
4401         this.el.on('click', this.onClick, this);
4402         
4403     },
4404     onClick : function(e)
4405     {
4406         Roo.log('PaginationItem on click ');
4407         if(this.preventDefault){
4408             e.preventDefault();
4409         }
4410         
4411         if(this.disabled){
4412             return;
4413         }
4414         
4415         this.fireEvent('click', this, e);
4416     }
4417    
4418 });
4419
4420  
4421
4422  /*
4423  * - LGPL
4424  *
4425  * slider
4426  * 
4427  */
4428
4429
4430 /**
4431  * @class Roo.bootstrap.Slider
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Slider class
4434  *    
4435  * @constructor
4436  * Create a new Slider
4437  * @param {Object} config The config object
4438  */
4439
4440 Roo.bootstrap.Slider = function(config){
4441     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4442 };
4443
4444 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4445     
4446     getAutoCreate : function(){
4447         
4448         var cfg = {
4449             tag: 'div',
4450             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4451             cn: [
4452                 {
4453                     tag: 'a',
4454                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4455                 }
4456             ]
4457         }
4458         
4459         return cfg;
4460     }
4461    
4462 });
4463
4464  /*
4465  * Based on:
4466  * Ext JS Library 1.1.1
4467  * Copyright(c) 2006-2007, Ext JS, LLC.
4468  *
4469  * Originally Released Under LGPL - original licence link has changed is not relivant.
4470  *
4471  * Fork - LGPL
4472  * <script type="text/javascript">
4473  */
4474  
4475
4476 /**
4477  * @class Roo.grid.ColumnModel
4478  * @extends Roo.util.Observable
4479  * This is the default implementation of a ColumnModel used by the Grid. It defines
4480  * the columns in the grid.
4481  * <br>Usage:<br>
4482  <pre><code>
4483  var colModel = new Roo.grid.ColumnModel([
4484         {header: "Ticker", width: 60, sortable: true, locked: true},
4485         {header: "Company Name", width: 150, sortable: true},
4486         {header: "Market Cap.", width: 100, sortable: true},
4487         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4488         {header: "Employees", width: 100, sortable: true, resizable: false}
4489  ]);
4490  </code></pre>
4491  * <p>
4492  
4493  * The config options listed for this class are options which may appear in each
4494  * individual column definition.
4495  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4496  * @constructor
4497  * @param {Object} config An Array of column config objects. See this class's
4498  * config objects for details.
4499 */
4500 Roo.grid.ColumnModel = function(config){
4501         /**
4502      * The config passed into the constructor
4503      */
4504     this.config = config;
4505     this.lookup = {};
4506
4507     // if no id, create one
4508     // if the column does not have a dataIndex mapping,
4509     // map it to the order it is in the config
4510     for(var i = 0, len = config.length; i < len; i++){
4511         var c = config[i];
4512         if(typeof c.dataIndex == "undefined"){
4513             c.dataIndex = i;
4514         }
4515         if(typeof c.renderer == "string"){
4516             c.renderer = Roo.util.Format[c.renderer];
4517         }
4518         if(typeof c.id == "undefined"){
4519             c.id = Roo.id();
4520         }
4521         if(c.editor && c.editor.xtype){
4522             c.editor  = Roo.factory(c.editor, Roo.grid);
4523         }
4524         if(c.editor && c.editor.isFormField){
4525             c.editor = new Roo.grid.GridEditor(c.editor);
4526         }
4527         this.lookup[c.id] = c;
4528     }
4529
4530     /**
4531      * The width of columns which have no width specified (defaults to 100)
4532      * @type Number
4533      */
4534     this.defaultWidth = 100;
4535
4536     /**
4537      * Default sortable of columns which have no sortable specified (defaults to false)
4538      * @type Boolean
4539      */
4540     this.defaultSortable = false;
4541
4542     this.addEvents({
4543         /**
4544              * @event widthchange
4545              * Fires when the width of a column changes.
4546              * @param {ColumnModel} this
4547              * @param {Number} columnIndex The column index
4548              * @param {Number} newWidth The new width
4549              */
4550             "widthchange": true,
4551         /**
4552              * @event headerchange
4553              * Fires when the text of a header changes.
4554              * @param {ColumnModel} this
4555              * @param {Number} columnIndex The column index
4556              * @param {Number} newText The new header text
4557              */
4558             "headerchange": true,
4559         /**
4560              * @event hiddenchange
4561              * Fires when a column is hidden or "unhidden".
4562              * @param {ColumnModel} this
4563              * @param {Number} columnIndex The column index
4564              * @param {Boolean} hidden true if hidden, false otherwise
4565              */
4566             "hiddenchange": true,
4567             /**
4568          * @event columnmoved
4569          * Fires when a column is moved.
4570          * @param {ColumnModel} this
4571          * @param {Number} oldIndex
4572          * @param {Number} newIndex
4573          */
4574         "columnmoved" : true,
4575         /**
4576          * @event columlockchange
4577          * Fires when a column's locked state is changed
4578          * @param {ColumnModel} this
4579          * @param {Number} colIndex
4580          * @param {Boolean} locked true if locked
4581          */
4582         "columnlockchange" : true
4583     });
4584     Roo.grid.ColumnModel.superclass.constructor.call(this);
4585 };
4586 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4587     /**
4588      * @cfg {String} header The header text to display in the Grid view.
4589      */
4590     /**
4591      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4592      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4593      * specified, the column's index is used as an index into the Record's data Array.
4594      */
4595     /**
4596      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4597      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4598      */
4599     /**
4600      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4601      * Defaults to the value of the {@link #defaultSortable} property.
4602      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4603      */
4604     /**
4605      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4606      */
4607     /**
4608      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4609      */
4610     /**
4611      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4612      */
4613     /**
4614      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4615      */
4616     /**
4617      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4618      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4619      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4620      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4621      */
4622        /**
4623      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4624      */
4625     /**
4626      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4627      */
4628     /**
4629      * @cfg {String} cursor (Optional)
4630      */
4631     /**
4632      * @cfg {String} tooltip (Optional)
4633      */
4634     /**
4635      * Returns the id of the column at the specified index.
4636      * @param {Number} index The column index
4637      * @return {String} the id
4638      */
4639     getColumnId : function(index){
4640         return this.config[index].id;
4641     },
4642
4643     /**
4644      * Returns the column for a specified id.
4645      * @param {String} id The column id
4646      * @return {Object} the column
4647      */
4648     getColumnById : function(id){
4649         return this.lookup[id];
4650     },
4651
4652     
4653     /**
4654      * Returns the column for a specified dataIndex.
4655      * @param {String} dataIndex The column dataIndex
4656      * @return {Object|Boolean} the column or false if not found
4657      */
4658     getColumnByDataIndex: function(dataIndex){
4659         var index = this.findColumnIndex(dataIndex);
4660         return index > -1 ? this.config[index] : false;
4661     },
4662     
4663     /**
4664      * Returns the index for a specified column id.
4665      * @param {String} id The column id
4666      * @return {Number} the index, or -1 if not found
4667      */
4668     getIndexById : function(id){
4669         for(var i = 0, len = this.config.length; i < len; i++){
4670             if(this.config[i].id == id){
4671                 return i;
4672             }
4673         }
4674         return -1;
4675     },
4676     
4677     /**
4678      * Returns the index for a specified column dataIndex.
4679      * @param {String} dataIndex The column dataIndex
4680      * @return {Number} the index, or -1 if not found
4681      */
4682     
4683     findColumnIndex : function(dataIndex){
4684         for(var i = 0, len = this.config.length; i < len; i++){
4685             if(this.config[i].dataIndex == dataIndex){
4686                 return i;
4687             }
4688         }
4689         return -1;
4690     },
4691     
4692     
4693     moveColumn : function(oldIndex, newIndex){
4694         var c = this.config[oldIndex];
4695         this.config.splice(oldIndex, 1);
4696         this.config.splice(newIndex, 0, c);
4697         this.dataMap = null;
4698         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4699     },
4700
4701     isLocked : function(colIndex){
4702         return this.config[colIndex].locked === true;
4703     },
4704
4705     setLocked : function(colIndex, value, suppressEvent){
4706         if(this.isLocked(colIndex) == value){
4707             return;
4708         }
4709         this.config[colIndex].locked = value;
4710         if(!suppressEvent){
4711             this.fireEvent("columnlockchange", this, colIndex, value);
4712         }
4713     },
4714
4715     getTotalLockedWidth : function(){
4716         var totalWidth = 0;
4717         for(var i = 0; i < this.config.length; i++){
4718             if(this.isLocked(i) && !this.isHidden(i)){
4719                 this.totalWidth += this.getColumnWidth(i);
4720             }
4721         }
4722         return totalWidth;
4723     },
4724
4725     getLockedCount : function(){
4726         for(var i = 0, len = this.config.length; i < len; i++){
4727             if(!this.isLocked(i)){
4728                 return i;
4729             }
4730         }
4731     },
4732
4733     /**
4734      * Returns the number of columns.
4735      * @return {Number}
4736      */
4737     getColumnCount : function(visibleOnly){
4738         if(visibleOnly === true){
4739             var c = 0;
4740             for(var i = 0, len = this.config.length; i < len; i++){
4741                 if(!this.isHidden(i)){
4742                     c++;
4743                 }
4744             }
4745             return c;
4746         }
4747         return this.config.length;
4748     },
4749
4750     /**
4751      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4752      * @param {Function} fn
4753      * @param {Object} scope (optional)
4754      * @return {Array} result
4755      */
4756     getColumnsBy : function(fn, scope){
4757         var r = [];
4758         for(var i = 0, len = this.config.length; i < len; i++){
4759             var c = this.config[i];
4760             if(fn.call(scope||this, c, i) === true){
4761                 r[r.length] = c;
4762             }
4763         }
4764         return r;
4765     },
4766
4767     /**
4768      * Returns true if the specified column is sortable.
4769      * @param {Number} col The column index
4770      * @return {Boolean}
4771      */
4772     isSortable : function(col){
4773         if(typeof this.config[col].sortable == "undefined"){
4774             return this.defaultSortable;
4775         }
4776         return this.config[col].sortable;
4777     },
4778
4779     /**
4780      * Returns the rendering (formatting) function defined for the column.
4781      * @param {Number} col The column index.
4782      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4783      */
4784     getRenderer : function(col){
4785         if(!this.config[col].renderer){
4786             return Roo.grid.ColumnModel.defaultRenderer;
4787         }
4788         return this.config[col].renderer;
4789     },
4790
4791     /**
4792      * Sets the rendering (formatting) function for a column.
4793      * @param {Number} col The column index
4794      * @param {Function} fn The function to use to process the cell's raw data
4795      * to return HTML markup for the grid view. The render function is called with
4796      * the following parameters:<ul>
4797      * <li>Data value.</li>
4798      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4799      * <li>css A CSS style string to apply to the table cell.</li>
4800      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4801      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4802      * <li>Row index</li>
4803      * <li>Column index</li>
4804      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4805      */
4806     setRenderer : function(col, fn){
4807         this.config[col].renderer = fn;
4808     },
4809
4810     /**
4811      * Returns the width for the specified column.
4812      * @param {Number} col The column index
4813      * @return {Number}
4814      */
4815     getColumnWidth : function(col){
4816         return this.config[col].width * 1 || this.defaultWidth;
4817     },
4818
4819     /**
4820      * Sets the width for a column.
4821      * @param {Number} col The column index
4822      * @param {Number} width The new width
4823      */
4824     setColumnWidth : function(col, width, suppressEvent){
4825         this.config[col].width = width;
4826         this.totalWidth = null;
4827         if(!suppressEvent){
4828              this.fireEvent("widthchange", this, col, width);
4829         }
4830     },
4831
4832     /**
4833      * Returns the total width of all columns.
4834      * @param {Boolean} includeHidden True to include hidden column widths
4835      * @return {Number}
4836      */
4837     getTotalWidth : function(includeHidden){
4838         if(!this.totalWidth){
4839             this.totalWidth = 0;
4840             for(var i = 0, len = this.config.length; i < len; i++){
4841                 if(includeHidden || !this.isHidden(i)){
4842                     this.totalWidth += this.getColumnWidth(i);
4843                 }
4844             }
4845         }
4846         return this.totalWidth;
4847     },
4848
4849     /**
4850      * Returns the header for the specified column.
4851      * @param {Number} col The column index
4852      * @return {String}
4853      */
4854     getColumnHeader : function(col){
4855         return this.config[col].header;
4856     },
4857
4858     /**
4859      * Sets the header for a column.
4860      * @param {Number} col The column index
4861      * @param {String} header The new header
4862      */
4863     setColumnHeader : function(col, header){
4864         this.config[col].header = header;
4865         this.fireEvent("headerchange", this, col, header);
4866     },
4867
4868     /**
4869      * Returns the tooltip for the specified column.
4870      * @param {Number} col The column index
4871      * @return {String}
4872      */
4873     getColumnTooltip : function(col){
4874             return this.config[col].tooltip;
4875     },
4876     /**
4877      * Sets the tooltip for a column.
4878      * @param {Number} col The column index
4879      * @param {String} tooltip The new tooltip
4880      */
4881     setColumnTooltip : function(col, tooltip){
4882             this.config[col].tooltip = tooltip;
4883     },
4884
4885     /**
4886      * Returns the dataIndex for the specified column.
4887      * @param {Number} col The column index
4888      * @return {Number}
4889      */
4890     getDataIndex : function(col){
4891         return this.config[col].dataIndex;
4892     },
4893
4894     /**
4895      * Sets the dataIndex for a column.
4896      * @param {Number} col The column index
4897      * @param {Number} dataIndex The new dataIndex
4898      */
4899     setDataIndex : function(col, dataIndex){
4900         this.config[col].dataIndex = dataIndex;
4901     },
4902
4903     
4904     
4905     /**
4906      * Returns true if the cell is editable.
4907      * @param {Number} colIndex The column index
4908      * @param {Number} rowIndex The row index
4909      * @return {Boolean}
4910      */
4911     isCellEditable : function(colIndex, rowIndex){
4912         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4913     },
4914
4915     /**
4916      * Returns the editor defined for the cell/column.
4917      * return false or null to disable editing.
4918      * @param {Number} colIndex The column index
4919      * @param {Number} rowIndex The row index
4920      * @return {Object}
4921      */
4922     getCellEditor : function(colIndex, rowIndex){
4923         return this.config[colIndex].editor;
4924     },
4925
4926     /**
4927      * Sets if a column is editable.
4928      * @param {Number} col The column index
4929      * @param {Boolean} editable True if the column is editable
4930      */
4931     setEditable : function(col, editable){
4932         this.config[col].editable = editable;
4933     },
4934
4935
4936     /**
4937      * Returns true if the column is hidden.
4938      * @param {Number} colIndex The column index
4939      * @return {Boolean}
4940      */
4941     isHidden : function(colIndex){
4942         return this.config[colIndex].hidden;
4943     },
4944
4945
4946     /**
4947      * Returns true if the column width cannot be changed
4948      */
4949     isFixed : function(colIndex){
4950         return this.config[colIndex].fixed;
4951     },
4952
4953     /**
4954      * Returns true if the column can be resized
4955      * @return {Boolean}
4956      */
4957     isResizable : function(colIndex){
4958         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4959     },
4960     /**
4961      * Sets if a column is hidden.
4962      * @param {Number} colIndex The column index
4963      * @param {Boolean} hidden True if the column is hidden
4964      */
4965     setHidden : function(colIndex, hidden){
4966         this.config[colIndex].hidden = hidden;
4967         this.totalWidth = null;
4968         this.fireEvent("hiddenchange", this, colIndex, hidden);
4969     },
4970
4971     /**
4972      * Sets the editor for a column.
4973      * @param {Number} col The column index
4974      * @param {Object} editor The editor object
4975      */
4976     setEditor : function(col, editor){
4977         this.config[col].editor = editor;
4978     }
4979 });
4980
4981 Roo.grid.ColumnModel.defaultRenderer = function(value){
4982         if(typeof value == "string" && value.length < 1){
4983             return "&#160;";
4984         }
4985         return value;
4986 };
4987
4988 // Alias for backwards compatibility
4989 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4990 /*
4991  * Based on:
4992  * Ext JS Library 1.1.1
4993  * Copyright(c) 2006-2007, Ext JS, LLC.
4994  *
4995  * Originally Released Under LGPL - original licence link has changed is not relivant.
4996  *
4997  * Fork - LGPL
4998  * <script type="text/javascript">
4999  */
5000  
5001 /**
5002  * @class Roo.LoadMask
5003  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5004  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5005  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5006  * element's UpdateManager load indicator and will be destroyed after the initial load.
5007  * @constructor
5008  * Create a new LoadMask
5009  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5010  * @param {Object} config The config object
5011  */
5012 Roo.LoadMask = function(el, config){
5013     this.el = Roo.get(el);
5014     Roo.apply(this, config);
5015     if(this.store){
5016         this.store.on('beforeload', this.onBeforeLoad, this);
5017         this.store.on('load', this.onLoad, this);
5018         this.store.on('loadexception', this.onLoadException, this);
5019         this.removeMask = false;
5020     }else{
5021         var um = this.el.getUpdateManager();
5022         um.showLoadIndicator = false; // disable the default indicator
5023         um.on('beforeupdate', this.onBeforeLoad, this);
5024         um.on('update', this.onLoad, this);
5025         um.on('failure', this.onLoad, this);
5026         this.removeMask = true;
5027     }
5028 };
5029
5030 Roo.LoadMask.prototype = {
5031     /**
5032      * @cfg {Boolean} removeMask
5033      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5034      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5035      */
5036     /**
5037      * @cfg {String} msg
5038      * The text to display in a centered loading message box (defaults to 'Loading...')
5039      */
5040     msg : 'Loading...',
5041     /**
5042      * @cfg {String} msgCls
5043      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5044      */
5045     msgCls : 'x-mask-loading',
5046
5047     /**
5048      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5049      * @type Boolean
5050      */
5051     disabled: false,
5052
5053     /**
5054      * Disables the mask to prevent it from being displayed
5055      */
5056     disable : function(){
5057        this.disabled = true;
5058     },
5059
5060     /**
5061      * Enables the mask so that it can be displayed
5062      */
5063     enable : function(){
5064         this.disabled = false;
5065     },
5066     
5067     onLoadException : function()
5068     {
5069         Roo.log(arguments);
5070         
5071         if (typeof(arguments[3]) != 'undefined') {
5072             Roo.MessageBox.alert("Error loading",arguments[3]);
5073         } 
5074         /*
5075         try {
5076             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5077                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5078             }   
5079         } catch(e) {
5080             
5081         }
5082         */
5083     
5084         
5085         
5086         this.el.unmask(this.removeMask);
5087     },
5088     // private
5089     onLoad : function()
5090     {
5091         this.el.unmask(this.removeMask);
5092     },
5093
5094     // private
5095     onBeforeLoad : function(){
5096         if(!this.disabled){
5097             this.el.mask(this.msg, this.msgCls);
5098         }
5099     },
5100
5101     // private
5102     destroy : function(){
5103         if(this.store){
5104             this.store.un('beforeload', this.onBeforeLoad, this);
5105             this.store.un('load', this.onLoad, this);
5106             this.store.un('loadexception', this.onLoadException, this);
5107         }else{
5108             var um = this.el.getUpdateManager();
5109             um.un('beforeupdate', this.onBeforeLoad, this);
5110             um.un('update', this.onLoad, this);
5111             um.un('failure', this.onLoad, this);
5112         }
5113     }
5114 };/*
5115  * - LGPL
5116  *
5117  * table
5118  * 
5119  */
5120
5121 /**
5122  * @class Roo.bootstrap.Table
5123  * @extends Roo.bootstrap.Component
5124  * Bootstrap Table class
5125  * @cfg {String} cls table class
5126  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5127  * @cfg {String} bgcolor Specifies the background color for a table
5128  * @cfg {Number} border Specifies whether the table cells should have borders or not
5129  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5130  * @cfg {Number} cellspacing Specifies the space between cells
5131  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5132  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5133  * @cfg {String} sortable Specifies that the table should be sortable
5134  * @cfg {String} summary Specifies a summary of the content of a table
5135  * @cfg {Number} width Specifies the width of a table
5136  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5137  * 
5138  * @cfg {boolean} striped Should the rows be alternative striped
5139  * @cfg {boolean} bordered Add borders to the table
5140  * @cfg {boolean} hover Add hover highlighting
5141  * @cfg {boolean} condensed Format condensed
5142  * @cfg {boolean} responsive Format condensed
5143  * @cfg {Boolean} loadMask (true|false) default false
5144  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5145  * @cfg {Boolean} thead (true|false) generate thead, default true
5146  * @cfg {Boolean} RowSelection (true|false) default false
5147  * @cfg {Boolean} CellSelection (true|false) default false
5148  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5149  
5150  * 
5151  * @constructor
5152  * Create a new Table
5153  * @param {Object} config The config object
5154  */
5155
5156 Roo.bootstrap.Table = function(config){
5157     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5158     
5159     if (this.sm) {
5160         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5161         this.sm = this.selModel;
5162         this.sm.xmodule = this.xmodule || false;
5163     }
5164     if (this.cm && typeof(this.cm.config) == 'undefined') {
5165         this.colModel = new Roo.grid.ColumnModel(this.cm);
5166         this.cm = this.colModel;
5167         this.cm.xmodule = this.xmodule || false;
5168     }
5169     if (this.store) {
5170         this.store= Roo.factory(this.store, Roo.data);
5171         this.ds = this.store;
5172         this.ds.xmodule = this.xmodule || false;
5173          
5174     }
5175     if (this.footer && this.store) {
5176         this.footer.dataSource = this.ds;
5177         this.footer = Roo.factory(this.footer);
5178     }
5179     
5180     /** @private */
5181     this.addEvents({
5182         /**
5183          * @event cellclick
5184          * Fires when a cell is clicked
5185          * @param {Roo.bootstrap.Table} this
5186          * @param {Roo.Element} el
5187          * @param {Number} rowIndex
5188          * @param {Number} columnIndex
5189          * @param {Roo.EventObject} e
5190          */
5191         "cellclick" : true,
5192         /**
5193          * @event celldblclick
5194          * Fires when a cell is double clicked
5195          * @param {Roo.bootstrap.Table} this
5196          * @param {Roo.Element} el
5197          * @param {Number} rowIndex
5198          * @param {Number} columnIndex
5199          * @param {Roo.EventObject} e
5200          */
5201         "celldblclick" : true,
5202         /**
5203          * @event rowclick
5204          * Fires when a row is clicked
5205          * @param {Roo.bootstrap.Table} this
5206          * @param {Roo.Element} el
5207          * @param {Number} rowIndex
5208          * @param {Roo.EventObject} e
5209          */
5210         "rowclick" : true,
5211         /**
5212          * @event rowdblclick
5213          * Fires when a row is double clicked
5214          * @param {Roo.bootstrap.Table} this
5215          * @param {Roo.Element} el
5216          * @param {Number} rowIndex
5217          * @param {Roo.EventObject} e
5218          */
5219         "rowdblclick" : true,
5220         /**
5221          * @event mouseover
5222          * Fires when a mouseover occur
5223          * @param {Roo.bootstrap.Table} this
5224          * @param {Roo.Element} el
5225          * @param {Number} rowIndex
5226          * @param {Number} columnIndex
5227          * @param {Roo.EventObject} e
5228          */
5229         "mouseover" : true,
5230         /**
5231          * @event mouseout
5232          * Fires when a mouseout occur
5233          * @param {Roo.bootstrap.Table} this
5234          * @param {Roo.Element} el
5235          * @param {Number} rowIndex
5236          * @param {Number} columnIndex
5237          * @param {Roo.EventObject} e
5238          */
5239         "mouseout" : true,
5240         /**
5241          * @event rowclass
5242          * Fires when a row is rendered, so you can change add a style to it.
5243          * @param {Roo.bootstrap.Table} this
5244          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5245          */
5246         'rowclass' : true,
5247           /**
5248          * @event rowsrendered
5249          * Fires when all the  rows have been rendered
5250          * @param {Roo.bootstrap.Table} this
5251          */
5252         'rowsrendered' : true
5253         
5254     });
5255 };
5256
5257 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5258     
5259     cls: false,
5260     align: false,
5261     bgcolor: false,
5262     border: false,
5263     cellpadding: false,
5264     cellspacing: false,
5265     frame: false,
5266     rules: false,
5267     sortable: false,
5268     summary: false,
5269     width: false,
5270     striped : false,
5271     bordered: false,
5272     hover:  false,
5273     condensed : false,
5274     responsive : false,
5275     sm : false,
5276     cm : false,
5277     store : false,
5278     loadMask : false,
5279     tfoot : true,
5280     thead : true,
5281     RowSelection : false,
5282     CellSelection : false,
5283     layout : false,
5284     
5285     // Roo.Element - the tbody
5286     mainBody: false, 
5287     
5288     getAutoCreate : function(){
5289         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5290         
5291         cfg = {
5292             tag: 'table',
5293             cls : 'table',
5294             cn : []
5295         }
5296             
5297         if (this.striped) {
5298             cfg.cls += ' table-striped';
5299         }
5300         
5301         if (this.hover) {
5302             cfg.cls += ' table-hover';
5303         }
5304         if (this.bordered) {
5305             cfg.cls += ' table-bordered';
5306         }
5307         if (this.condensed) {
5308             cfg.cls += ' table-condensed';
5309         }
5310         if (this.responsive) {
5311             cfg.cls += ' table-responsive';
5312         }
5313         
5314         if (this.cls) {
5315             cfg.cls+=  ' ' +this.cls;
5316         }
5317         
5318         // this lot should be simplifed...
5319         
5320         if (this.align) {
5321             cfg.align=this.align;
5322         }
5323         if (this.bgcolor) {
5324             cfg.bgcolor=this.bgcolor;
5325         }
5326         if (this.border) {
5327             cfg.border=this.border;
5328         }
5329         if (this.cellpadding) {
5330             cfg.cellpadding=this.cellpadding;
5331         }
5332         if (this.cellspacing) {
5333             cfg.cellspacing=this.cellspacing;
5334         }
5335         if (this.frame) {
5336             cfg.frame=this.frame;
5337         }
5338         if (this.rules) {
5339             cfg.rules=this.rules;
5340         }
5341         if (this.sortable) {
5342             cfg.sortable=this.sortable;
5343         }
5344         if (this.summary) {
5345             cfg.summary=this.summary;
5346         }
5347         if (this.width) {
5348             cfg.width=this.width;
5349         }
5350         if (this.layout) {
5351             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5352         }
5353         
5354         if(this.store || this.cm){
5355             if(this.thead){
5356                 cfg.cn.push(this.renderHeader());
5357             }
5358             
5359             cfg.cn.push(this.renderBody());
5360             
5361             if(this.tfoot){
5362                 cfg.cn.push(this.renderFooter());
5363             }
5364             
5365             cfg.cls+=  ' TableGrid';
5366         }
5367         
5368         return { cn : [ cfg ] };
5369     },
5370     
5371     initEvents : function()
5372     {   
5373         if(!this.store || !this.cm){
5374             return;
5375         }
5376         
5377         //Roo.log('initEvents with ds!!!!');
5378         
5379         this.mainBody = this.el.select('tbody', true).first();
5380         
5381         
5382         var _this = this;
5383         
5384         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5385             e.on('click', _this.sort, _this);
5386         });
5387         
5388         this.el.on("click", this.onClick, this);
5389         this.el.on("dblclick", this.onDblClick, this);
5390         
5391         // why is this done????? = it breaks dialogs??
5392         //this.parent().el.setStyle('position', 'relative');
5393         
5394         
5395         if (this.footer) {
5396             this.footer.parentId = this.id;
5397             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5398         }
5399         
5400         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5401         
5402         this.store.on('load', this.onLoad, this);
5403         this.store.on('beforeload', this.onBeforeLoad, this);
5404         this.store.on('update', this.onUpdate, this);
5405         this.store.on('add', this.onAdd, this);
5406         
5407     },
5408     
5409     onMouseover : function(e, el)
5410     {
5411         var cell = Roo.get(el);
5412         
5413         if(!cell){
5414             return;
5415         }
5416         
5417         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5418             cell = cell.findParent('td', false, true);
5419         }
5420         
5421         var row = cell.findParent('tr', false, true);
5422         var cellIndex = cell.dom.cellIndex;
5423         var rowIndex = row.dom.rowIndex - 1; // start from 0
5424         
5425         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5426         
5427     },
5428     
5429     onMouseout : function(e, el)
5430     {
5431         var cell = Roo.get(el);
5432         
5433         if(!cell){
5434             return;
5435         }
5436         
5437         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5438             cell = cell.findParent('td', false, true);
5439         }
5440         
5441         var row = cell.findParent('tr', false, true);
5442         var cellIndex = cell.dom.cellIndex;
5443         var rowIndex = row.dom.rowIndex - 1; // start from 0
5444         
5445         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5446         
5447     },
5448     
5449     onClick : function(e, el)
5450     {
5451         var cell = Roo.get(el);
5452         
5453         if(!cell || (!this.CellSelection && !this.RowSelection)){
5454             return;
5455         }
5456         
5457         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5458             cell = cell.findParent('td', false, true);
5459         }
5460         
5461         if(!cell || typeof(cell) == 'undefined'){
5462             return;
5463         }
5464         
5465         var row = cell.findParent('tr', false, true);
5466         
5467         if(!row || typeof(row) == 'undefined'){
5468             return;
5469         }
5470         
5471         var cellIndex = cell.dom.cellIndex;
5472         var rowIndex = this.getRowIndex(row);
5473         
5474         if(this.CellSelection){
5475             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5476         }
5477         
5478         if(this.RowSelection){
5479             this.fireEvent('rowclick', this, row, rowIndex, e);
5480         }
5481         
5482         
5483     },
5484     
5485     onDblClick : function(e,el)
5486     {
5487         var cell = Roo.get(el);
5488         
5489         if(!cell || (!this.CellSelection && !this.RowSelection)){
5490             return;
5491         }
5492         
5493         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5494             cell = cell.findParent('td', false, true);
5495         }
5496         
5497         if(!cell || typeof(cell) == 'undefined'){
5498             return;
5499         }
5500         
5501         var row = cell.findParent('tr', false, true);
5502         
5503         if(!row || typeof(row) == 'undefined'){
5504             return;
5505         }
5506         
5507         var cellIndex = cell.dom.cellIndex;
5508         var rowIndex = this.getRowIndex(row);
5509         
5510         if(this.CellSelection){
5511             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5512         }
5513         
5514         if(this.RowSelection){
5515             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5516         }
5517     },
5518     
5519     sort : function(e,el)
5520     {
5521         var col = Roo.get(el);
5522         
5523         if(!col.hasClass('sortable')){
5524             return;
5525         }
5526         
5527         var sort = col.attr('sort');
5528         var dir = 'ASC';
5529         
5530         if(col.hasClass('glyphicon-arrow-up')){
5531             dir = 'DESC';
5532         }
5533         
5534         this.store.sortInfo = {field : sort, direction : dir};
5535         
5536         if (this.footer) {
5537             Roo.log("calling footer first");
5538             this.footer.onClick('first');
5539         } else {
5540         
5541             this.store.load({ params : { start : 0 } });
5542         }
5543     },
5544     
5545     renderHeader : function()
5546     {
5547         var header = {
5548             tag: 'thead',
5549             cn : []
5550         };
5551         
5552         var cm = this.cm;
5553         
5554         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5555             
5556             var config = cm.config[i];
5557                     
5558             var c = {
5559                 tag: 'th',
5560                 style : '',
5561                 html: cm.getColumnHeader(i)
5562             };
5563             
5564             if(typeof(config.tooltip) != 'undefined'){
5565                 c.tooltip = config.tooltip;
5566             }
5567             
5568             if(typeof(config.colspan) != 'undefined'){
5569                 c.colspan = config.colspan;
5570             }
5571             
5572             if(typeof(config.hidden) != 'undefined' && config.hidden){
5573                 c.style += ' display:none;';
5574             }
5575             
5576             if(typeof(config.dataIndex) != 'undefined'){
5577                 c.sort = config.dataIndex;
5578             }
5579             
5580             if(typeof(config.sortable) != 'undefined' && config.sortable){
5581                 c.cls = 'sortable';
5582             }
5583             
5584             if(typeof(config.align) != 'undefined' && config.align.length){
5585                 c.style += ' text-align:' + config.align + ';';
5586             }
5587             
5588             if(typeof(config.width) != 'undefined'){
5589                 c.style += ' width:' + config.width + 'px;';
5590             }
5591             
5592             if(typeof(config.cls) != 'undefined'){
5593                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5594             }
5595             
5596             header.cn.push(c)
5597         }
5598         
5599         return header;
5600     },
5601     
5602     renderBody : function()
5603     {
5604         var body = {
5605             tag: 'tbody',
5606             cn : [
5607                 {
5608                     tag: 'tr',
5609                     cn : [
5610                         {
5611                             tag : 'td',
5612                             colspan :  this.cm.getColumnCount()
5613                         }
5614                     ]
5615                 }
5616             ]
5617         };
5618         
5619         return body;
5620     },
5621     
5622     renderFooter : function()
5623     {
5624         var footer = {
5625             tag: 'tfoot',
5626             cn : [
5627                 {
5628                     tag: 'tr',
5629                     cn : [
5630                         {
5631                             tag : 'td',
5632                             colspan :  this.cm.getColumnCount()
5633                         }
5634                     ]
5635                 }
5636             ]
5637         };
5638         
5639         return footer;
5640     },
5641     
5642     
5643     
5644     onLoad : function()
5645     {
5646         Roo.log('ds onload');
5647         this.clear();
5648         
5649         var _this = this;
5650         var cm = this.cm;
5651         var ds = this.store;
5652         
5653         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5654             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5655             
5656             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5657                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5658             }
5659             
5660             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5661                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5662             }
5663         });
5664         
5665         var tbody =  this.mainBody;
5666               
5667         if(ds.getCount() > 0){
5668             ds.data.each(function(d,rowIndex){
5669                 var row =  this.renderRow(cm, ds, rowIndex);
5670                 
5671                 tbody.createChild(row);
5672                 
5673                 var _this = this;
5674                 
5675                 if(row.cellObjects.length){
5676                     Roo.each(row.cellObjects, function(r){
5677                         _this.renderCellObject(r);
5678                     })
5679                 }
5680                 
5681             }, this);
5682         }
5683         
5684         Roo.each(this.el.select('tbody td', true).elements, function(e){
5685             e.on('mouseover', _this.onMouseover, _this);
5686         });
5687         
5688         Roo.each(this.el.select('tbody td', true).elements, function(e){
5689             e.on('mouseout', _this.onMouseout, _this);
5690         });
5691         this.fireEvent('rowsrendered', this);
5692         //if(this.loadMask){
5693         //    this.maskEl.hide();
5694         //}
5695     },
5696     
5697     
5698     onUpdate : function(ds,record)
5699     {
5700         this.refreshRow(record);
5701     },
5702     
5703     onRemove : function(ds, record, index, isUpdate){
5704         if(isUpdate !== true){
5705             this.fireEvent("beforerowremoved", this, index, record);
5706         }
5707         var bt = this.mainBody.dom;
5708         
5709         var rows = this.el.select('tbody > tr', true).elements;
5710         
5711         if(typeof(rows[index]) != 'undefined'){
5712             bt.removeChild(rows[index].dom);
5713         }
5714         
5715 //        if(bt.rows[index]){
5716 //            bt.removeChild(bt.rows[index]);
5717 //        }
5718         
5719         if(isUpdate !== true){
5720             //this.stripeRows(index);
5721             //this.syncRowHeights(index, index);
5722             //this.layout();
5723             this.fireEvent("rowremoved", this, index, record);
5724         }
5725     },
5726     
5727     onAdd : function(ds, records, rowIndex)
5728     {
5729         //Roo.log('on Add called');
5730         // - note this does not handle multiple adding very well..
5731         var bt = this.mainBody.dom;
5732         for (var i =0 ; i < records.length;i++) {
5733             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5734             //Roo.log(records[i]);
5735             //Roo.log(this.store.getAt(rowIndex+i));
5736             this.insertRow(this.store, rowIndex + i, false);
5737             return;
5738         }
5739         
5740     },
5741     
5742     
5743     refreshRow : function(record){
5744         var ds = this.store, index;
5745         if(typeof record == 'number'){
5746             index = record;
5747             record = ds.getAt(index);
5748         }else{
5749             index = ds.indexOf(record);
5750         }
5751         this.insertRow(ds, index, true);
5752         this.onRemove(ds, record, index+1, true);
5753         //this.syncRowHeights(index, index);
5754         //this.layout();
5755         this.fireEvent("rowupdated", this, index, record);
5756     },
5757     
5758     insertRow : function(dm, rowIndex, isUpdate){
5759         
5760         if(!isUpdate){
5761             this.fireEvent("beforerowsinserted", this, rowIndex);
5762         }
5763             //var s = this.getScrollState();
5764         var row = this.renderRow(this.cm, this.store, rowIndex);
5765         // insert before rowIndex..
5766         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5767         
5768         var _this = this;
5769                 
5770         if(row.cellObjects.length){
5771             Roo.each(row.cellObjects, function(r){
5772                 _this.renderCellObject(r);
5773             })
5774         }
5775             
5776         if(!isUpdate){
5777             this.fireEvent("rowsinserted", this, rowIndex);
5778             //this.syncRowHeights(firstRow, lastRow);
5779             //this.stripeRows(firstRow);
5780             //this.layout();
5781         }
5782         
5783     },
5784     
5785     
5786     getRowDom : function(rowIndex)
5787     {
5788         var rows = this.el.select('tbody > tr', true).elements;
5789         
5790         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5791         
5792     },
5793     // returns the object tree for a tr..
5794   
5795     
5796     renderRow : function(cm, ds, rowIndex) 
5797     {
5798         
5799         var d = ds.getAt(rowIndex);
5800         
5801         var row = {
5802             tag : 'tr',
5803             cn : []
5804         };
5805             
5806         var cellObjects = [];
5807         
5808         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5809             var config = cm.config[i];
5810             
5811             var renderer = cm.getRenderer(i);
5812             var value = '';
5813             var id = false;
5814             
5815             if(typeof(renderer) !== 'undefined'){
5816                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5817             }
5818             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5819             // and are rendered into the cells after the row is rendered - using the id for the element.
5820             
5821             if(typeof(value) === 'object'){
5822                 id = Roo.id();
5823                 cellObjects.push({
5824                     container : id,
5825                     cfg : value 
5826                 })
5827             }
5828             
5829             var rowcfg = {
5830                 record: d,
5831                 rowIndex : rowIndex,
5832                 colIndex : i,
5833                 rowClass : ''
5834             }
5835
5836             this.fireEvent('rowclass', this, rowcfg);
5837             
5838             var td = {
5839                 tag: 'td',
5840                 cls : rowcfg.rowClass,
5841                 style: '',
5842                 html: (typeof(value) === 'object') ? '' : value
5843             };
5844             
5845             if (id) {
5846                 td.id = id;
5847             }
5848             
5849             if(typeof(config.colspan) != 'undefined'){
5850                 td.colspan = config.colspan;
5851             }
5852             
5853             if(typeof(config.hidden) != 'undefined' && config.hidden){
5854                 td.style += ' display:none;';
5855             }
5856             
5857             if(typeof(config.align) != 'undefined' && config.align.length){
5858                 td.style += ' text-align:' + config.align + ';';
5859             }
5860             
5861             if(typeof(config.width) != 'undefined'){
5862                 td.style += ' width:' +  config.width + 'px;';
5863             }
5864             
5865             if(typeof(config.cursor) != 'undefined'){
5866                 td.style += ' cursor:' +  config.cursor + ';';
5867             }
5868             
5869             if(typeof(config.cls) != 'undefined'){
5870                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5871             }
5872              
5873             row.cn.push(td);
5874            
5875         }
5876         
5877         row.cellObjects = cellObjects;
5878         
5879         return row;
5880           
5881     },
5882     
5883     
5884     
5885     onBeforeLoad : function()
5886     {
5887         //Roo.log('ds onBeforeLoad');
5888         
5889         //this.clear();
5890         
5891         //if(this.loadMask){
5892         //    this.maskEl.show();
5893         //}
5894     },
5895      /**
5896      * Remove all rows
5897      */
5898     clear : function()
5899     {
5900         this.el.select('tbody', true).first().dom.innerHTML = '';
5901     },
5902     /**
5903      * Show or hide a row.
5904      * @param {Number} rowIndex to show or hide
5905      * @param {Boolean} state hide
5906      */
5907     setRowVisibility : function(rowIndex, state)
5908     {
5909         var bt = this.mainBody.dom;
5910         
5911         var rows = this.el.select('tbody > tr', true).elements;
5912         
5913         if(typeof(rows[rowIndex]) == 'undefined'){
5914             return;
5915         }
5916         rows[rowIndex].dom.style.display = state ? '' : 'none';
5917     },
5918     
5919     
5920     getSelectionModel : function(){
5921         if(!this.selModel){
5922             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5923         }
5924         return this.selModel;
5925     },
5926     /*
5927      * Render the Roo.bootstrap object from renderder
5928      */
5929     renderCellObject : function(r)
5930     {
5931         var _this = this;
5932         
5933         var t = r.cfg.render(r.container);
5934         
5935         if(r.cfg.cn){
5936             Roo.each(r.cfg.cn, function(c){
5937                 var child = {
5938                     container: t.getChildContainer(),
5939                     cfg: c
5940                 }
5941                 _this.renderCellObject(child);
5942             })
5943         }
5944     },
5945     
5946     getRowIndex : function(row)
5947     {
5948         var rowIndex = -1;
5949         
5950         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5951             if(el != row){
5952                 return;
5953             }
5954             
5955             rowIndex = index;
5956         });
5957         
5958         return rowIndex;
5959     }
5960    
5961 });
5962
5963  
5964
5965  /*
5966  * - LGPL
5967  *
5968  * table cell
5969  * 
5970  */
5971
5972 /**
5973  * @class Roo.bootstrap.TableCell
5974  * @extends Roo.bootstrap.Component
5975  * Bootstrap TableCell class
5976  * @cfg {String} html cell contain text
5977  * @cfg {String} cls cell class
5978  * @cfg {String} tag cell tag (td|th) default td
5979  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5980  * @cfg {String} align Aligns the content in a cell
5981  * @cfg {String} axis Categorizes cells
5982  * @cfg {String} bgcolor Specifies the background color of a cell
5983  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5984  * @cfg {Number} colspan Specifies the number of columns a cell should span
5985  * @cfg {String} headers Specifies one or more header cells a cell is related to
5986  * @cfg {Number} height Sets the height of a cell
5987  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5988  * @cfg {Number} rowspan Sets the number of rows a cell should span
5989  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5990  * @cfg {String} valign Vertical aligns the content in a cell
5991  * @cfg {Number} width Specifies the width of a cell
5992  * 
5993  * @constructor
5994  * Create a new TableCell
5995  * @param {Object} config The config object
5996  */
5997
5998 Roo.bootstrap.TableCell = function(config){
5999     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6000 };
6001
6002 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6003     
6004     html: false,
6005     cls: false,
6006     tag: false,
6007     abbr: false,
6008     align: false,
6009     axis: false,
6010     bgcolor: false,
6011     charoff: false,
6012     colspan: false,
6013     headers: false,
6014     height: false,
6015     nowrap: false,
6016     rowspan: false,
6017     scope: false,
6018     valign: false,
6019     width: false,
6020     
6021     
6022     getAutoCreate : function(){
6023         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6024         
6025         cfg = {
6026             tag: 'td'
6027         }
6028         
6029         if(this.tag){
6030             cfg.tag = this.tag;
6031         }
6032         
6033         if (this.html) {
6034             cfg.html=this.html
6035         }
6036         if (this.cls) {
6037             cfg.cls=this.cls
6038         }
6039         if (this.abbr) {
6040             cfg.abbr=this.abbr
6041         }
6042         if (this.align) {
6043             cfg.align=this.align
6044         }
6045         if (this.axis) {
6046             cfg.axis=this.axis
6047         }
6048         if (this.bgcolor) {
6049             cfg.bgcolor=this.bgcolor
6050         }
6051         if (this.charoff) {
6052             cfg.charoff=this.charoff
6053         }
6054         if (this.colspan) {
6055             cfg.colspan=this.colspan
6056         }
6057         if (this.headers) {
6058             cfg.headers=this.headers
6059         }
6060         if (this.height) {
6061             cfg.height=this.height
6062         }
6063         if (this.nowrap) {
6064             cfg.nowrap=this.nowrap
6065         }
6066         if (this.rowspan) {
6067             cfg.rowspan=this.rowspan
6068         }
6069         if (this.scope) {
6070             cfg.scope=this.scope
6071         }
6072         if (this.valign) {
6073             cfg.valign=this.valign
6074         }
6075         if (this.width) {
6076             cfg.width=this.width
6077         }
6078         
6079         
6080         return cfg;
6081     }
6082    
6083 });
6084
6085  
6086
6087  /*
6088  * - LGPL
6089  *
6090  * table row
6091  * 
6092  */
6093
6094 /**
6095  * @class Roo.bootstrap.TableRow
6096  * @extends Roo.bootstrap.Component
6097  * Bootstrap TableRow class
6098  * @cfg {String} cls row class
6099  * @cfg {String} align Aligns the content in a table row
6100  * @cfg {String} bgcolor Specifies a background color for a table row
6101  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6102  * @cfg {String} valign Vertical aligns the content in a table row
6103  * 
6104  * @constructor
6105  * Create a new TableRow
6106  * @param {Object} config The config object
6107  */
6108
6109 Roo.bootstrap.TableRow = function(config){
6110     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6111 };
6112
6113 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6114     
6115     cls: false,
6116     align: false,
6117     bgcolor: false,
6118     charoff: false,
6119     valign: false,
6120     
6121     getAutoCreate : function(){
6122         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6123         
6124         cfg = {
6125             tag: 'tr'
6126         }
6127             
6128         if(this.cls){
6129             cfg.cls = this.cls;
6130         }
6131         if(this.align){
6132             cfg.align = this.align;
6133         }
6134         if(this.bgcolor){
6135             cfg.bgcolor = this.bgcolor;
6136         }
6137         if(this.charoff){
6138             cfg.charoff = this.charoff;
6139         }
6140         if(this.valign){
6141             cfg.valign = this.valign;
6142         }
6143         
6144         return cfg;
6145     }
6146    
6147 });
6148
6149  
6150
6151  /*
6152  * - LGPL
6153  *
6154  * table body
6155  * 
6156  */
6157
6158 /**
6159  * @class Roo.bootstrap.TableBody
6160  * @extends Roo.bootstrap.Component
6161  * Bootstrap TableBody class
6162  * @cfg {String} cls element class
6163  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6164  * @cfg {String} align Aligns the content inside the element
6165  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6166  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6167  * 
6168  * @constructor
6169  * Create a new TableBody
6170  * @param {Object} config The config object
6171  */
6172
6173 Roo.bootstrap.TableBody = function(config){
6174     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6175 };
6176
6177 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6178     
6179     cls: false,
6180     tag: false,
6181     align: false,
6182     charoff: false,
6183     valign: false,
6184     
6185     getAutoCreate : function(){
6186         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6187         
6188         cfg = {
6189             tag: 'tbody'
6190         }
6191             
6192         if (this.cls) {
6193             cfg.cls=this.cls
6194         }
6195         if(this.tag){
6196             cfg.tag = this.tag;
6197         }
6198         
6199         if(this.align){
6200             cfg.align = this.align;
6201         }
6202         if(this.charoff){
6203             cfg.charoff = this.charoff;
6204         }
6205         if(this.valign){
6206             cfg.valign = this.valign;
6207         }
6208         
6209         return cfg;
6210     }
6211     
6212     
6213 //    initEvents : function()
6214 //    {
6215 //        
6216 //        if(!this.store){
6217 //            return;
6218 //        }
6219 //        
6220 //        this.store = Roo.factory(this.store, Roo.data);
6221 //        this.store.on('load', this.onLoad, this);
6222 //        
6223 //        this.store.load();
6224 //        
6225 //    },
6226 //    
6227 //    onLoad: function () 
6228 //    {   
6229 //        this.fireEvent('load', this);
6230 //    }
6231 //    
6232 //   
6233 });
6234
6235  
6236
6237  /*
6238  * Based on:
6239  * Ext JS Library 1.1.1
6240  * Copyright(c) 2006-2007, Ext JS, LLC.
6241  *
6242  * Originally Released Under LGPL - original licence link has changed is not relivant.
6243  *
6244  * Fork - LGPL
6245  * <script type="text/javascript">
6246  */
6247
6248 // as we use this in bootstrap.
6249 Roo.namespace('Roo.form');
6250  /**
6251  * @class Roo.form.Action
6252  * Internal Class used to handle form actions
6253  * @constructor
6254  * @param {Roo.form.BasicForm} el The form element or its id
6255  * @param {Object} config Configuration options
6256  */
6257
6258  
6259  
6260 // define the action interface
6261 Roo.form.Action = function(form, options){
6262     this.form = form;
6263     this.options = options || {};
6264 };
6265 /**
6266  * Client Validation Failed
6267  * @const 
6268  */
6269 Roo.form.Action.CLIENT_INVALID = 'client';
6270 /**
6271  * Server Validation Failed
6272  * @const 
6273  */
6274 Roo.form.Action.SERVER_INVALID = 'server';
6275  /**
6276  * Connect to Server Failed
6277  * @const 
6278  */
6279 Roo.form.Action.CONNECT_FAILURE = 'connect';
6280 /**
6281  * Reading Data from Server Failed
6282  * @const 
6283  */
6284 Roo.form.Action.LOAD_FAILURE = 'load';
6285
6286 Roo.form.Action.prototype = {
6287     type : 'default',
6288     failureType : undefined,
6289     response : undefined,
6290     result : undefined,
6291
6292     // interface method
6293     run : function(options){
6294
6295     },
6296
6297     // interface method
6298     success : function(response){
6299
6300     },
6301
6302     // interface method
6303     handleResponse : function(response){
6304
6305     },
6306
6307     // default connection failure
6308     failure : function(response){
6309         
6310         this.response = response;
6311         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6312         this.form.afterAction(this, false);
6313     },
6314
6315     processResponse : function(response){
6316         this.response = response;
6317         if(!response.responseText){
6318             return true;
6319         }
6320         this.result = this.handleResponse(response);
6321         return this.result;
6322     },
6323
6324     // utility functions used internally
6325     getUrl : function(appendParams){
6326         var url = this.options.url || this.form.url || this.form.el.dom.action;
6327         if(appendParams){
6328             var p = this.getParams();
6329             if(p){
6330                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6331             }
6332         }
6333         return url;
6334     },
6335
6336     getMethod : function(){
6337         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6338     },
6339
6340     getParams : function(){
6341         var bp = this.form.baseParams;
6342         var p = this.options.params;
6343         if(p){
6344             if(typeof p == "object"){
6345                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6346             }else if(typeof p == 'string' && bp){
6347                 p += '&' + Roo.urlEncode(bp);
6348             }
6349         }else if(bp){
6350             p = Roo.urlEncode(bp);
6351         }
6352         return p;
6353     },
6354
6355     createCallback : function(){
6356         return {
6357             success: this.success,
6358             failure: this.failure,
6359             scope: this,
6360             timeout: (this.form.timeout*1000),
6361             upload: this.form.fileUpload ? this.success : undefined
6362         };
6363     }
6364 };
6365
6366 Roo.form.Action.Submit = function(form, options){
6367     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6368 };
6369
6370 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6371     type : 'submit',
6372
6373     haveProgress : false,
6374     uploadComplete : false,
6375     
6376     // uploadProgress indicator.
6377     uploadProgress : function()
6378     {
6379         if (!this.form.progressUrl) {
6380             return;
6381         }
6382         
6383         if (!this.haveProgress) {
6384             Roo.MessageBox.progress("Uploading", "Uploading");
6385         }
6386         if (this.uploadComplete) {
6387            Roo.MessageBox.hide();
6388            return;
6389         }
6390         
6391         this.haveProgress = true;
6392    
6393         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6394         
6395         var c = new Roo.data.Connection();
6396         c.request({
6397             url : this.form.progressUrl,
6398             params: {
6399                 id : uid
6400             },
6401             method: 'GET',
6402             success : function(req){
6403                //console.log(data);
6404                 var rdata = false;
6405                 var edata;
6406                 try  {
6407                    rdata = Roo.decode(req.responseText)
6408                 } catch (e) {
6409                     Roo.log("Invalid data from server..");
6410                     Roo.log(edata);
6411                     return;
6412                 }
6413                 if (!rdata || !rdata.success) {
6414                     Roo.log(rdata);
6415                     Roo.MessageBox.alert(Roo.encode(rdata));
6416                     return;
6417                 }
6418                 var data = rdata.data;
6419                 
6420                 if (this.uploadComplete) {
6421                    Roo.MessageBox.hide();
6422                    return;
6423                 }
6424                    
6425                 if (data){
6426                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6427                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6428                     );
6429                 }
6430                 this.uploadProgress.defer(2000,this);
6431             },
6432        
6433             failure: function(data) {
6434                 Roo.log('progress url failed ');
6435                 Roo.log(data);
6436             },
6437             scope : this
6438         });
6439            
6440     },
6441     
6442     
6443     run : function()
6444     {
6445         // run get Values on the form, so it syncs any secondary forms.
6446         this.form.getValues();
6447         
6448         var o = this.options;
6449         var method = this.getMethod();
6450         var isPost = method == 'POST';
6451         if(o.clientValidation === false || this.form.isValid()){
6452             
6453             if (this.form.progressUrl) {
6454                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6455                     (new Date() * 1) + '' + Math.random());
6456                     
6457             } 
6458             
6459             
6460             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6461                 form:this.form.el.dom,
6462                 url:this.getUrl(!isPost),
6463                 method: method,
6464                 params:isPost ? this.getParams() : null,
6465                 isUpload: this.form.fileUpload
6466             }));
6467             
6468             this.uploadProgress();
6469
6470         }else if (o.clientValidation !== false){ // client validation failed
6471             this.failureType = Roo.form.Action.CLIENT_INVALID;
6472             this.form.afterAction(this, false);
6473         }
6474     },
6475
6476     success : function(response)
6477     {
6478         this.uploadComplete= true;
6479         if (this.haveProgress) {
6480             Roo.MessageBox.hide();
6481         }
6482         
6483         
6484         var result = this.processResponse(response);
6485         if(result === true || result.success){
6486             this.form.afterAction(this, true);
6487             return;
6488         }
6489         if(result.errors){
6490             this.form.markInvalid(result.errors);
6491             this.failureType = Roo.form.Action.SERVER_INVALID;
6492         }
6493         this.form.afterAction(this, false);
6494     },
6495     failure : function(response)
6496     {
6497         this.uploadComplete= true;
6498         if (this.haveProgress) {
6499             Roo.MessageBox.hide();
6500         }
6501         
6502         this.response = response;
6503         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6504         this.form.afterAction(this, false);
6505     },
6506     
6507     handleResponse : function(response){
6508         if(this.form.errorReader){
6509             var rs = this.form.errorReader.read(response);
6510             var errors = [];
6511             if(rs.records){
6512                 for(var i = 0, len = rs.records.length; i < len; i++) {
6513                     var r = rs.records[i];
6514                     errors[i] = r.data;
6515                 }
6516             }
6517             if(errors.length < 1){
6518                 errors = null;
6519             }
6520             return {
6521                 success : rs.success,
6522                 errors : errors
6523             };
6524         }
6525         var ret = false;
6526         try {
6527             ret = Roo.decode(response.responseText);
6528         } catch (e) {
6529             ret = {
6530                 success: false,
6531                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6532                 errors : []
6533             };
6534         }
6535         return ret;
6536         
6537     }
6538 });
6539
6540
6541 Roo.form.Action.Load = function(form, options){
6542     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6543     this.reader = this.form.reader;
6544 };
6545
6546 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6547     type : 'load',
6548
6549     run : function(){
6550         
6551         Roo.Ajax.request(Roo.apply(
6552                 this.createCallback(), {
6553                     method:this.getMethod(),
6554                     url:this.getUrl(false),
6555                     params:this.getParams()
6556         }));
6557     },
6558
6559     success : function(response){
6560         
6561         var result = this.processResponse(response);
6562         if(result === true || !result.success || !result.data){
6563             this.failureType = Roo.form.Action.LOAD_FAILURE;
6564             this.form.afterAction(this, false);
6565             return;
6566         }
6567         this.form.clearInvalid();
6568         this.form.setValues(result.data);
6569         this.form.afterAction(this, true);
6570     },
6571
6572     handleResponse : function(response){
6573         if(this.form.reader){
6574             var rs = this.form.reader.read(response);
6575             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6576             return {
6577                 success : rs.success,
6578                 data : data
6579             };
6580         }
6581         return Roo.decode(response.responseText);
6582     }
6583 });
6584
6585 Roo.form.Action.ACTION_TYPES = {
6586     'load' : Roo.form.Action.Load,
6587     'submit' : Roo.form.Action.Submit
6588 };/*
6589  * - LGPL
6590  *
6591  * form
6592  * 
6593  */
6594
6595 /**
6596  * @class Roo.bootstrap.Form
6597  * @extends Roo.bootstrap.Component
6598  * Bootstrap Form class
6599  * @cfg {String} method  GET | POST (default POST)
6600  * @cfg {String} labelAlign top | left (default top)
6601  * @cfg {String} align left  | right - for navbars
6602  * @cfg {Boolean} loadMask load mask when submit (default true)
6603
6604  * 
6605  * @constructor
6606  * Create a new Form
6607  * @param {Object} config The config object
6608  */
6609
6610
6611 Roo.bootstrap.Form = function(config){
6612     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6613     this.addEvents({
6614         /**
6615          * @event clientvalidation
6616          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6617          * @param {Form} this
6618          * @param {Boolean} valid true if the form has passed client-side validation
6619          */
6620         clientvalidation: true,
6621         /**
6622          * @event beforeaction
6623          * Fires before any action is performed. Return false to cancel the action.
6624          * @param {Form} this
6625          * @param {Action} action The action to be performed
6626          */
6627         beforeaction: true,
6628         /**
6629          * @event actionfailed
6630          * Fires when an action fails.
6631          * @param {Form} this
6632          * @param {Action} action The action that failed
6633          */
6634         actionfailed : true,
6635         /**
6636          * @event actioncomplete
6637          * Fires when an action is completed.
6638          * @param {Form} this
6639          * @param {Action} action The action that completed
6640          */
6641         actioncomplete : true
6642     });
6643     
6644 };
6645
6646 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6647       
6648      /**
6649      * @cfg {String} method
6650      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6651      */
6652     method : 'POST',
6653     /**
6654      * @cfg {String} url
6655      * The URL to use for form actions if one isn't supplied in the action options.
6656      */
6657     /**
6658      * @cfg {Boolean} fileUpload
6659      * Set to true if this form is a file upload.
6660      */
6661      
6662     /**
6663      * @cfg {Object} baseParams
6664      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6665      */
6666       
6667     /**
6668      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6669      */
6670     timeout: 30,
6671     /**
6672      * @cfg {Sting} align (left|right) for navbar forms
6673      */
6674     align : 'left',
6675
6676     // private
6677     activeAction : null,
6678  
6679     /**
6680      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6681      * element by passing it or its id or mask the form itself by passing in true.
6682      * @type Mixed
6683      */
6684     waitMsgTarget : false,
6685     
6686     loadMask : true,
6687     
6688     getAutoCreate : function(){
6689         
6690         var cfg = {
6691             tag: 'form',
6692             method : this.method || 'POST',
6693             id : this.id || Roo.id(),
6694             cls : ''
6695         }
6696         if (this.parent().xtype.match(/^Nav/)) {
6697             cfg.cls = 'navbar-form navbar-' + this.align;
6698             
6699         }
6700         
6701         if (this.labelAlign == 'left' ) {
6702             cfg.cls += ' form-horizontal';
6703         }
6704         
6705         
6706         return cfg;
6707     },
6708     initEvents : function()
6709     {
6710         this.el.on('submit', this.onSubmit, this);
6711         // this was added as random key presses on the form where triggering form submit.
6712         this.el.on('keypress', function(e) {
6713             if (e.getCharCode() != 13) {
6714                 return true;
6715             }
6716             // we might need to allow it for textareas.. and some other items.
6717             // check e.getTarget().
6718             
6719             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6720                 return true;
6721             }
6722         
6723             Roo.log("keypress blocked");
6724             
6725             e.preventDefault();
6726             return false;
6727         });
6728         
6729     },
6730     // private
6731     onSubmit : function(e){
6732         e.stopEvent();
6733     },
6734     
6735      /**
6736      * Returns true if client-side validation on the form is successful.
6737      * @return Boolean
6738      */
6739     isValid : function(){
6740         var items = this.getItems();
6741         var valid = true;
6742         items.each(function(f){
6743            if(!f.validate()){
6744                valid = false;
6745                
6746            }
6747         });
6748         return valid;
6749     },
6750     /**
6751      * Returns true if any fields in this form have changed since their original load.
6752      * @return Boolean
6753      */
6754     isDirty : function(){
6755         var dirty = false;
6756         var items = this.getItems();
6757         items.each(function(f){
6758            if(f.isDirty()){
6759                dirty = true;
6760                return false;
6761            }
6762            return true;
6763         });
6764         return dirty;
6765     },
6766      /**
6767      * Performs a predefined action (submit or load) or custom actions you define on this form.
6768      * @param {String} actionName The name of the action type
6769      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6770      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6771      * accept other config options):
6772      * <pre>
6773 Property          Type             Description
6774 ----------------  ---------------  ----------------------------------------------------------------------------------
6775 url               String           The url for the action (defaults to the form's url)
6776 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6777 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6778 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6779                                    validate the form on the client (defaults to false)
6780      * </pre>
6781      * @return {BasicForm} this
6782      */
6783     doAction : function(action, options){
6784         if(typeof action == 'string'){
6785             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6786         }
6787         if(this.fireEvent('beforeaction', this, action) !== false){
6788             this.beforeAction(action);
6789             action.run.defer(100, action);
6790         }
6791         return this;
6792     },
6793     
6794     // private
6795     beforeAction : function(action){
6796         var o = action.options;
6797         
6798         if(this.loadMask){
6799             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6800         }
6801         // not really supported yet.. ??
6802         
6803         //if(this.waitMsgTarget === true){
6804         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6805         //}else if(this.waitMsgTarget){
6806         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6807         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6808         //}else {
6809         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6810        // }
6811          
6812     },
6813
6814     // private
6815     afterAction : function(action, success){
6816         this.activeAction = null;
6817         var o = action.options;
6818         
6819         //if(this.waitMsgTarget === true){
6820             this.el.unmask();
6821         //}else if(this.waitMsgTarget){
6822         //    this.waitMsgTarget.unmask();
6823         //}else{
6824         //    Roo.MessageBox.updateProgress(1);
6825         //    Roo.MessageBox.hide();
6826        // }
6827         // 
6828         if(success){
6829             if(o.reset){
6830                 this.reset();
6831             }
6832             Roo.callback(o.success, o.scope, [this, action]);
6833             this.fireEvent('actioncomplete', this, action);
6834             
6835         }else{
6836             
6837             // failure condition..
6838             // we have a scenario where updates need confirming.
6839             // eg. if a locking scenario exists..
6840             // we look for { errors : { needs_confirm : true }} in the response.
6841             if (
6842                 (typeof(action.result) != 'undefined')  &&
6843                 (typeof(action.result.errors) != 'undefined')  &&
6844                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6845            ){
6846                 var _t = this;
6847                 Roo.log("not supported yet");
6848                  /*
6849                 
6850                 Roo.MessageBox.confirm(
6851                     "Change requires confirmation",
6852                     action.result.errorMsg,
6853                     function(r) {
6854                         if (r != 'yes') {
6855                             return;
6856                         }
6857                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6858                     }
6859                     
6860                 );
6861                 */
6862                 
6863                 
6864                 return;
6865             }
6866             
6867             Roo.callback(o.failure, o.scope, [this, action]);
6868             // show an error message if no failed handler is set..
6869             if (!this.hasListener('actionfailed')) {
6870                 Roo.log("need to add dialog support");
6871                 /*
6872                 Roo.MessageBox.alert("Error",
6873                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6874                         action.result.errorMsg :
6875                         "Saving Failed, please check your entries or try again"
6876                 );
6877                 */
6878             }
6879             
6880             this.fireEvent('actionfailed', this, action);
6881         }
6882         
6883     },
6884     /**
6885      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6886      * @param {String} id The value to search for
6887      * @return Field
6888      */
6889     findField : function(id){
6890         var items = this.getItems();
6891         var field = items.get(id);
6892         if(!field){
6893              items.each(function(f){
6894                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6895                     field = f;
6896                     return false;
6897                 }
6898                 return true;
6899             });
6900         }
6901         return field || null;
6902     },
6903      /**
6904      * Mark fields in this form invalid in bulk.
6905      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6906      * @return {BasicForm} this
6907      */
6908     markInvalid : function(errors){
6909         if(errors instanceof Array){
6910             for(var i = 0, len = errors.length; i < len; i++){
6911                 var fieldError = errors[i];
6912                 var f = this.findField(fieldError.id);
6913                 if(f){
6914                     f.markInvalid(fieldError.msg);
6915                 }
6916             }
6917         }else{
6918             var field, id;
6919             for(id in errors){
6920                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6921                     field.markInvalid(errors[id]);
6922                 }
6923             }
6924         }
6925         //Roo.each(this.childForms || [], function (f) {
6926         //    f.markInvalid(errors);
6927         //});
6928         
6929         return this;
6930     },
6931
6932     /**
6933      * Set values for fields in this form in bulk.
6934      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6935      * @return {BasicForm} this
6936      */
6937     setValues : function(values){
6938         if(values instanceof Array){ // array of objects
6939             for(var i = 0, len = values.length; i < len; i++){
6940                 var v = values[i];
6941                 var f = this.findField(v.id);
6942                 if(f){
6943                     f.setValue(v.value);
6944                     if(this.trackResetOnLoad){
6945                         f.originalValue = f.getValue();
6946                     }
6947                 }
6948             }
6949         }else{ // object hash
6950             var field, id;
6951             for(id in values){
6952                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6953                     
6954                     if (field.setFromData && 
6955                         field.valueField && 
6956                         field.displayField &&
6957                         // combos' with local stores can 
6958                         // be queried via setValue()
6959                         // to set their value..
6960                         (field.store && !field.store.isLocal)
6961                         ) {
6962                         // it's a combo
6963                         var sd = { };
6964                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6965                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6966                         field.setFromData(sd);
6967                         
6968                     } else {
6969                         field.setValue(values[id]);
6970                     }
6971                     
6972                     
6973                     if(this.trackResetOnLoad){
6974                         field.originalValue = field.getValue();
6975                     }
6976                 }
6977             }
6978         }
6979          
6980         //Roo.each(this.childForms || [], function (f) {
6981         //    f.setValues(values);
6982         //});
6983                 
6984         return this;
6985     },
6986
6987     /**
6988      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6989      * they are returned as an array.
6990      * @param {Boolean} asString
6991      * @return {Object}
6992      */
6993     getValues : function(asString){
6994         //if (this.childForms) {
6995             // copy values from the child forms
6996         //    Roo.each(this.childForms, function (f) {
6997         //        this.setValues(f.getValues());
6998         //    }, this);
6999         //}
7000         
7001         
7002         
7003         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7004         if(asString === true){
7005             return fs;
7006         }
7007         return Roo.urlDecode(fs);
7008     },
7009     
7010     /**
7011      * Returns the fields in this form as an object with key/value pairs. 
7012      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7013      * @return {Object}
7014      */
7015     getFieldValues : function(with_hidden)
7016     {
7017         var items = this.getItems();
7018         var ret = {};
7019         items.each(function(f){
7020             if (!f.getName()) {
7021                 return;
7022             }
7023             var v = f.getValue();
7024             if (f.inputType =='radio') {
7025                 if (typeof(ret[f.getName()]) == 'undefined') {
7026                     ret[f.getName()] = ''; // empty..
7027                 }
7028                 
7029                 if (!f.el.dom.checked) {
7030                     return;
7031                     
7032                 }
7033                 v = f.el.dom.value;
7034                 
7035             }
7036             
7037             // not sure if this supported any more..
7038             if ((typeof(v) == 'object') && f.getRawValue) {
7039                 v = f.getRawValue() ; // dates..
7040             }
7041             // combo boxes where name != hiddenName...
7042             if (f.name != f.getName()) {
7043                 ret[f.name] = f.getRawValue();
7044             }
7045             ret[f.getName()] = v;
7046         });
7047         
7048         return ret;
7049     },
7050
7051     /**
7052      * Clears all invalid messages in this form.
7053      * @return {BasicForm} this
7054      */
7055     clearInvalid : function(){
7056         var items = this.getItems();
7057         
7058         items.each(function(f){
7059            f.clearInvalid();
7060         });
7061         
7062         
7063         
7064         return this;
7065     },
7066
7067     /**
7068      * Resets this form.
7069      * @return {BasicForm} this
7070      */
7071     reset : function(){
7072         var items = this.getItems();
7073         items.each(function(f){
7074             f.reset();
7075         });
7076         
7077         Roo.each(this.childForms || [], function (f) {
7078             f.reset();
7079         });
7080        
7081         
7082         return this;
7083     },
7084     getItems : function()
7085     {
7086         var r=new Roo.util.MixedCollection(false, function(o){
7087             return o.id || (o.id = Roo.id());
7088         });
7089         var iter = function(el) {
7090             if (el.inputEl) {
7091                 r.add(el);
7092             }
7093             if (!el.items) {
7094                 return;
7095             }
7096             Roo.each(el.items,function(e) {
7097                 iter(e);
7098             });
7099             
7100             
7101         };
7102         
7103         iter(this);
7104         return r;
7105         
7106         
7107         
7108         
7109     }
7110     
7111 });
7112
7113  
7114 /*
7115  * Based on:
7116  * Ext JS Library 1.1.1
7117  * Copyright(c) 2006-2007, Ext JS, LLC.
7118  *
7119  * Originally Released Under LGPL - original licence link has changed is not relivant.
7120  *
7121  * Fork - LGPL
7122  * <script type="text/javascript">
7123  */
7124 /**
7125  * @class Roo.form.VTypes
7126  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7127  * @singleton
7128  */
7129 Roo.form.VTypes = function(){
7130     // closure these in so they are only created once.
7131     var alpha = /^[a-zA-Z_]+$/;
7132     var alphanum = /^[a-zA-Z0-9_]+$/;
7133     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7134     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7135
7136     // All these messages and functions are configurable
7137     return {
7138         /**
7139          * The function used to validate email addresses
7140          * @param {String} value The email address
7141          */
7142         'email' : function(v){
7143             return email.test(v);
7144         },
7145         /**
7146          * The error text to display when the email validation function returns false
7147          * @type String
7148          */
7149         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7150         /**
7151          * The keystroke filter mask to be applied on email input
7152          * @type RegExp
7153          */
7154         'emailMask' : /[a-z0-9_\.\-@]/i,
7155
7156         /**
7157          * The function used to validate URLs
7158          * @param {String} value The URL
7159          */
7160         'url' : function(v){
7161             return url.test(v);
7162         },
7163         /**
7164          * The error text to display when the url validation function returns false
7165          * @type String
7166          */
7167         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7168         
7169         /**
7170          * The function used to validate alpha values
7171          * @param {String} value The value
7172          */
7173         'alpha' : function(v){
7174             return alpha.test(v);
7175         },
7176         /**
7177          * The error text to display when the alpha validation function returns false
7178          * @type String
7179          */
7180         'alphaText' : 'This field should only contain letters and _',
7181         /**
7182          * The keystroke filter mask to be applied on alpha input
7183          * @type RegExp
7184          */
7185         'alphaMask' : /[a-z_]/i,
7186
7187         /**
7188          * The function used to validate alphanumeric values
7189          * @param {String} value The value
7190          */
7191         'alphanum' : function(v){
7192             return alphanum.test(v);
7193         },
7194         /**
7195          * The error text to display when the alphanumeric validation function returns false
7196          * @type String
7197          */
7198         'alphanumText' : 'This field should only contain letters, numbers and _',
7199         /**
7200          * The keystroke filter mask to be applied on alphanumeric input
7201          * @type RegExp
7202          */
7203         'alphanumMask' : /[a-z0-9_]/i
7204     };
7205 }();/*
7206  * - LGPL
7207  *
7208  * Input
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.Input
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap Input class
7216  * @cfg {Boolean} disabled is it disabled
7217  * @cfg {String} fieldLabel - the label associated
7218  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7219  * @cfg {String} name name of the input
7220  * @cfg {string} fieldLabel - the label associated
7221  * @cfg {string}  inputType - input / file submit ...
7222  * @cfg {string} placeholder - placeholder to put in text.
7223  * @cfg {string}  before - input group add on before
7224  * @cfg {string} after - input group add on after
7225  * @cfg {string} size - (lg|sm) or leave empty..
7226  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7227  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7228  * @cfg {Number} md colspan out of 12 for computer-sized screens
7229  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7230  * @cfg {string} value default value of the input
7231  * @cfg {Number} labelWidth set the width of label (0-12)
7232  * @cfg {String} labelAlign (top|left)
7233  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7234  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7235
7236  * @cfg {String} align (left|center|right) Default left
7237  * 
7238  * 
7239  * 
7240  * @constructor
7241  * Create a new Input
7242  * @param {Object} config The config object
7243  */
7244
7245 Roo.bootstrap.Input = function(config){
7246     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7247    
7248         this.addEvents({
7249             /**
7250              * @event focus
7251              * Fires when this field receives input focus.
7252              * @param {Roo.form.Field} this
7253              */
7254             focus : true,
7255             /**
7256              * @event blur
7257              * Fires when this field loses input focus.
7258              * @param {Roo.form.Field} this
7259              */
7260             blur : true,
7261             /**
7262              * @event specialkey
7263              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7264              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7265              * @param {Roo.form.Field} this
7266              * @param {Roo.EventObject} e The event object
7267              */
7268             specialkey : true,
7269             /**
7270              * @event change
7271              * Fires just before the field blurs if the field value has changed.
7272              * @param {Roo.form.Field} this
7273              * @param {Mixed} newValue The new value
7274              * @param {Mixed} oldValue The original value
7275              */
7276             change : true,
7277             /**
7278              * @event invalid
7279              * Fires after the field has been marked as invalid.
7280              * @param {Roo.form.Field} this
7281              * @param {String} msg The validation message
7282              */
7283             invalid : true,
7284             /**
7285              * @event valid
7286              * Fires after the field has been validated with no errors.
7287              * @param {Roo.form.Field} this
7288              */
7289             valid : true,
7290              /**
7291              * @event keyup
7292              * Fires after the key up
7293              * @param {Roo.form.Field} this
7294              * @param {Roo.EventObject}  e The event Object
7295              */
7296             keyup : true
7297         });
7298 };
7299
7300 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7301      /**
7302      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7303       automatic validation (defaults to "keyup").
7304      */
7305     validationEvent : "keyup",
7306      /**
7307      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7308      */
7309     validateOnBlur : true,
7310     /**
7311      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7312      */
7313     validationDelay : 250,
7314      /**
7315      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7316      */
7317     focusClass : "x-form-focus",  // not needed???
7318     
7319        
7320     /**
7321      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7322      */
7323     invalidClass : "has-warning",
7324     
7325     /**
7326      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7327      */
7328     validClass : "has-success",
7329     
7330     /**
7331      * @cfg {Boolean} hasFeedback (true|false) default true
7332      */
7333     hasFeedback : true,
7334     
7335     /**
7336      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7337      */
7338     invalidFeedbackClass : "glyphicon-warning-sign",
7339     
7340     /**
7341      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7342      */
7343     validFeedbackClass : "glyphicon-ok",
7344     
7345     /**
7346      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7347      */
7348     selectOnFocus : false,
7349     
7350      /**
7351      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7352      */
7353     maskRe : null,
7354        /**
7355      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7356      */
7357     vtype : null,
7358     
7359       /**
7360      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7361      */
7362     disableKeyFilter : false,
7363     
7364        /**
7365      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7366      */
7367     disabled : false,
7368      /**
7369      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7370      */
7371     allowBlank : true,
7372     /**
7373      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7374      */
7375     blankText : "This field is required",
7376     
7377      /**
7378      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7379      */
7380     minLength : 0,
7381     /**
7382      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7383      */
7384     maxLength : Number.MAX_VALUE,
7385     /**
7386      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7387      */
7388     minLengthText : "The minimum length for this field is {0}",
7389     /**
7390      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7391      */
7392     maxLengthText : "The maximum length for this field is {0}",
7393   
7394     
7395     /**
7396      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7397      * If available, this function will be called only after the basic validators all return true, and will be passed the
7398      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7399      */
7400     validator : null,
7401     /**
7402      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7403      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7404      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7405      */
7406     regex : null,
7407     /**
7408      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7409      */
7410     regexText : "",
7411     
7412     autocomplete: false,
7413     
7414     
7415     fieldLabel : '',
7416     inputType : 'text',
7417     
7418     name : false,
7419     placeholder: false,
7420     before : false,
7421     after : false,
7422     size : false,
7423     hasFocus : false,
7424     preventMark: false,
7425     isFormField : true,
7426     value : '',
7427     labelWidth : 2,
7428     labelAlign : false,
7429     readOnly : false,
7430     align : false,
7431     formatedValue : false,
7432     
7433     parentLabelAlign : function()
7434     {
7435         var parent = this;
7436         while (parent.parent()) {
7437             parent = parent.parent();
7438             if (typeof(parent.labelAlign) !='undefined') {
7439                 return parent.labelAlign;
7440             }
7441         }
7442         return 'left';
7443         
7444     },
7445     
7446     getAutoCreate : function(){
7447         
7448         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7449         
7450         var id = Roo.id();
7451         
7452         var cfg = {};
7453         
7454         if(this.inputType != 'hidden'){
7455             cfg.cls = 'form-group' //input-group
7456         }
7457         
7458         var input =  {
7459             tag: 'input',
7460             id : id,
7461             type : this.inputType,
7462             value : this.value,
7463             cls : 'form-control',
7464             placeholder : this.placeholder || '',
7465             autocomplete : this.autocomplete || 'new-password'
7466         };
7467         
7468         
7469         if(this.align){
7470             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7471         }
7472         
7473         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7474             input.maxLength = this.maxLength;
7475         }
7476         
7477         if (this.disabled) {
7478             input.disabled=true;
7479         }
7480         
7481         if (this.readOnly) {
7482             input.readonly=true;
7483         }
7484         
7485         if (this.name) {
7486             input.name = this.name;
7487         }
7488         if (this.size) {
7489             input.cls += ' input-' + this.size;
7490         }
7491         var settings=this;
7492         ['xs','sm','md','lg'].map(function(size){
7493             if (settings[size]) {
7494                 cfg.cls += ' col-' + size + '-' + settings[size];
7495             }
7496         });
7497         
7498         var inputblock = input;
7499         
7500         var feedback = {
7501             tag: 'span',
7502             cls: 'glyphicon form-control-feedback'
7503         };
7504             
7505         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7506             
7507             inputblock = {
7508                 cls : 'has-feedback',
7509                 cn :  [
7510                     input,
7511                     feedback
7512                 ] 
7513             };  
7514         }
7515         
7516         if (this.before || this.after) {
7517             
7518             inputblock = {
7519                 cls : 'input-group',
7520                 cn :  [] 
7521             };
7522             
7523             if (this.before && typeof(this.before) == 'string') {
7524                 
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'roo-input-before input-group-addon',
7528                     html : this.before
7529                 });
7530             }
7531             if (this.before && typeof(this.before) == 'object') {
7532                 this.before = Roo.factory(this.before);
7533                 Roo.log(this.before);
7534                 inputblock.cn.push({
7535                     tag :'span',
7536                     cls : 'roo-input-before input-group-' +
7537                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7538                 });
7539             }
7540             
7541             inputblock.cn.push(input);
7542             
7543             if (this.after && typeof(this.after) == 'string') {
7544                 inputblock.cn.push({
7545                     tag :'span',
7546                     cls : 'roo-input-after input-group-addon',
7547                     html : this.after
7548                 });
7549             }
7550             if (this.after && typeof(this.after) == 'object') {
7551                 this.after = Roo.factory(this.after);
7552                 Roo.log(this.after);
7553                 inputblock.cn.push({
7554                     tag :'span',
7555                     cls : 'roo-input-after input-group-' +
7556                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7557                 });
7558             }
7559             
7560             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7561                 inputblock.cls += ' has-feedback';
7562                 inputblock.cn.push(feedback);
7563             }
7564         };
7565         
7566         if (align ==='left' && this.fieldLabel.length) {
7567                 Roo.log("left and has label");
7568                 cfg.cn = [
7569                     
7570                     {
7571                         tag: 'label',
7572                         'for' :  id,
7573                         cls : 'control-label col-sm-' + this.labelWidth,
7574                         html : this.fieldLabel
7575                         
7576                     },
7577                     {
7578                         cls : "col-sm-" + (12 - this.labelWidth), 
7579                         cn: [
7580                             inputblock
7581                         ]
7582                     }
7583                     
7584                 ];
7585         } else if ( this.fieldLabel.length) {
7586                 Roo.log(" label");
7587                  cfg.cn = [
7588                    
7589                     {
7590                         tag: 'label',
7591                         //cls : 'input-group-addon',
7592                         html : this.fieldLabel
7593                         
7594                     },
7595                     
7596                     inputblock
7597                     
7598                 ];
7599
7600         } else {
7601             
7602                 Roo.log(" no label && no align");
7603                 cfg.cn = [
7604                     
7605                         inputblock
7606                     
7607                 ];
7608                 
7609                 
7610         };
7611         Roo.log('input-parentType: ' + this.parentType);
7612         
7613         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7614            cfg.cls += ' navbar-form';
7615            Roo.log(cfg);
7616         }
7617         
7618         return cfg;
7619         
7620     },
7621     /**
7622      * return the real input element.
7623      */
7624     inputEl: function ()
7625     {
7626         return this.el.select('input.form-control',true).first();
7627     },
7628     
7629     tooltipEl : function()
7630     {
7631         return this.inputEl();
7632     },
7633     
7634     setDisabled : function(v)
7635     {
7636         var i  = this.inputEl().dom;
7637         if (!v) {
7638             i.removeAttribute('disabled');
7639             return;
7640             
7641         }
7642         i.setAttribute('disabled','true');
7643     },
7644     initEvents : function()
7645     {
7646           
7647         this.inputEl().on("keydown" , this.fireKey,  this);
7648         this.inputEl().on("focus", this.onFocus,  this);
7649         this.inputEl().on("blur", this.onBlur,  this);
7650         
7651         this.inputEl().relayEvent('keyup', this);
7652
7653         // reference to original value for reset
7654         this.originalValue = this.getValue();
7655         //Roo.form.TextField.superclass.initEvents.call(this);
7656         if(this.validationEvent == 'keyup'){
7657             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7658             this.inputEl().on('keyup', this.filterValidation, this);
7659         }
7660         else if(this.validationEvent !== false){
7661             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7662         }
7663         
7664         if(this.selectOnFocus){
7665             this.on("focus", this.preFocus, this);
7666             
7667         }
7668         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7669             this.inputEl().on("keypress", this.filterKeys, this);
7670         }
7671        /* if(this.grow){
7672             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7673             this.el.on("click", this.autoSize,  this);
7674         }
7675         */
7676         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7677             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7678         }
7679         
7680         if (typeof(this.before) == 'object') {
7681             this.before.render(this.el.select('.roo-input-before',true).first());
7682         }
7683         if (typeof(this.after) == 'object') {
7684             this.after.render(this.el.select('.roo-input-after',true).first());
7685         }
7686         
7687         
7688     },
7689     filterValidation : function(e){
7690         if(!e.isNavKeyPress()){
7691             this.validationTask.delay(this.validationDelay);
7692         }
7693     },
7694      /**
7695      * Validates the field value
7696      * @return {Boolean} True if the value is valid, else false
7697      */
7698     validate : function(){
7699         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7700         if(this.disabled || this.validateValue(this.getRawValue())){
7701             this.markValid();
7702             return true;
7703         }
7704         
7705         this.markInvalid();
7706         return false;
7707     },
7708     
7709     
7710     /**
7711      * Validates a value according to the field's validation rules and marks the field as invalid
7712      * if the validation fails
7713      * @param {Mixed} value The value to validate
7714      * @return {Boolean} True if the value is valid, else false
7715      */
7716     validateValue : function(value){
7717         if(value.length < 1)  { // if it's blank
7718             if(this.allowBlank){
7719                 return true;
7720             }
7721             return false;
7722         }
7723         
7724         if(value.length < this.minLength){
7725             return false;
7726         }
7727         if(value.length > this.maxLength){
7728             return false;
7729         }
7730         if(this.vtype){
7731             var vt = Roo.form.VTypes;
7732             if(!vt[this.vtype](value, this)){
7733                 return false;
7734             }
7735         }
7736         if(typeof this.validator == "function"){
7737             var msg = this.validator(value);
7738             if(msg !== true){
7739                 return false;
7740             }
7741         }
7742         
7743         if(this.regex && !this.regex.test(value)){
7744             return false;
7745         }
7746         
7747         return true;
7748     },
7749
7750     
7751     
7752      // private
7753     fireKey : function(e){
7754         //Roo.log('field ' + e.getKey());
7755         if(e.isNavKeyPress()){
7756             this.fireEvent("specialkey", this, e);
7757         }
7758     },
7759     focus : function (selectText){
7760         if(this.rendered){
7761             this.inputEl().focus();
7762             if(selectText === true){
7763                 this.inputEl().dom.select();
7764             }
7765         }
7766         return this;
7767     } ,
7768     
7769     onFocus : function(){
7770         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7771            // this.el.addClass(this.focusClass);
7772         }
7773         if(!this.hasFocus){
7774             this.hasFocus = true;
7775             this.startValue = this.getValue();
7776             this.fireEvent("focus", this);
7777         }
7778     },
7779     
7780     beforeBlur : Roo.emptyFn,
7781
7782     
7783     // private
7784     onBlur : function(){
7785         this.beforeBlur();
7786         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7787             //this.el.removeClass(this.focusClass);
7788         }
7789         this.hasFocus = false;
7790         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7791             this.validate();
7792         }
7793         var v = this.getValue();
7794         if(String(v) !== String(this.startValue)){
7795             this.fireEvent('change', this, v, this.startValue);
7796         }
7797         this.fireEvent("blur", this);
7798     },
7799     
7800     /**
7801      * Resets the current field value to the originally loaded value and clears any validation messages
7802      */
7803     reset : function(){
7804         this.setValue(this.originalValue);
7805         this.validate();
7806     },
7807      /**
7808      * Returns the name of the field
7809      * @return {Mixed} name The name field
7810      */
7811     getName: function(){
7812         return this.name;
7813     },
7814      /**
7815      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7816      * @return {Mixed} value The field value
7817      */
7818     getValue : function(){
7819         
7820         var v = this.inputEl().getValue();
7821         
7822         return v;
7823     },
7824     /**
7825      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7826      * @return {Mixed} value The field value
7827      */
7828     getRawValue : function(){
7829         var v = this.inputEl().getValue();
7830         
7831         return v;
7832     },
7833     
7834     /**
7835      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7836      * @param {Mixed} value The value to set
7837      */
7838     setRawValue : function(v){
7839         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7840     },
7841     
7842     selectText : function(start, end){
7843         var v = this.getRawValue();
7844         if(v.length > 0){
7845             start = start === undefined ? 0 : start;
7846             end = end === undefined ? v.length : end;
7847             var d = this.inputEl().dom;
7848             if(d.setSelectionRange){
7849                 d.setSelectionRange(start, end);
7850             }else if(d.createTextRange){
7851                 var range = d.createTextRange();
7852                 range.moveStart("character", start);
7853                 range.moveEnd("character", v.length-end);
7854                 range.select();
7855             }
7856         }
7857     },
7858     
7859     /**
7860      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7861      * @param {Mixed} value The value to set
7862      */
7863     setValue : function(v){
7864         this.value = v;
7865         if(this.rendered){
7866             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7867             this.validate();
7868         }
7869     },
7870     
7871     /*
7872     processValue : function(value){
7873         if(this.stripCharsRe){
7874             var newValue = value.replace(this.stripCharsRe, '');
7875             if(newValue !== value){
7876                 this.setRawValue(newValue);
7877                 return newValue;
7878             }
7879         }
7880         return value;
7881     },
7882   */
7883     preFocus : function(){
7884         
7885         if(this.selectOnFocus){
7886             this.inputEl().dom.select();
7887         }
7888     },
7889     filterKeys : function(e){
7890         var k = e.getKey();
7891         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7892             return;
7893         }
7894         var c = e.getCharCode(), cc = String.fromCharCode(c);
7895         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7896             return;
7897         }
7898         if(!this.maskRe.test(cc)){
7899             e.stopEvent();
7900         }
7901     },
7902      /**
7903      * Clear any invalid styles/messages for this field
7904      */
7905     clearInvalid : function(){
7906         
7907         if(!this.el || this.preventMark){ // not rendered
7908             return;
7909         }
7910         this.el.removeClass(this.invalidClass);
7911         
7912         this.fireEvent('valid', this);
7913     },
7914     
7915      /**
7916      * Mark this field as valid
7917      */
7918     markValid : function(){
7919         if(!this.el  || this.preventMark){ // not rendered
7920             return;
7921         }
7922         
7923         this.el.removeClass([this.invalidClass, this.validClass]);
7924         
7925         if(this.disabled || this.allowBlank){
7926             return;
7927         }
7928         
7929         this.el.addClass(this.validClass);
7930         
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7932             
7933             var feedback = this.el.select('.form-control-feedback', true).first();
7934             
7935             if(feedback){
7936                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7937                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7938             }
7939             
7940         }
7941         
7942         this.fireEvent('valid', this);
7943     },
7944     
7945      /**
7946      * Mark this field as invalid
7947      * @param {String} msg The validation message
7948      */
7949     markInvalid : function(msg){
7950         if(!this.el  || this.preventMark){ // not rendered
7951             return;
7952         }
7953         
7954         this.el.removeClass([this.invalidClass, this.validClass]);
7955         
7956         if(this.disabled || this.allowBlank){
7957             return;
7958         }
7959         
7960         this.el.addClass(this.invalidClass);
7961         
7962         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7963             
7964             var feedback = this.el.select('.form-control-feedback', true).first();
7965             
7966             if(feedback){
7967                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7968                 
7969                 if(this.getValue().length){
7970                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7971                 }
7972                 
7973             }
7974             
7975         }
7976         
7977         this.fireEvent('invalid', this, msg);
7978     },
7979     // private
7980     SafariOnKeyDown : function(event)
7981     {
7982         // this is a workaround for a password hang bug on chrome/ webkit.
7983         
7984         var isSelectAll = false;
7985         
7986         if(this.inputEl().dom.selectionEnd > 0){
7987             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7988         }
7989         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7990             event.preventDefault();
7991             this.setValue('');
7992             return;
7993         }
7994         
7995         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7996             
7997             event.preventDefault();
7998             // this is very hacky as keydown always get's upper case.
7999             //
8000             var cc = String.fromCharCode(event.getCharCode());
8001             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8002             
8003         }
8004     },
8005     adjustWidth : function(tag, w){
8006         tag = tag.toLowerCase();
8007         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8008             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8009                 if(tag == 'input'){
8010                     return w + 2;
8011                 }
8012                 if(tag == 'textarea'){
8013                     return w-2;
8014                 }
8015             }else if(Roo.isOpera){
8016                 if(tag == 'input'){
8017                     return w + 2;
8018                 }
8019                 if(tag == 'textarea'){
8020                     return w-2;
8021                 }
8022             }
8023         }
8024         return w;
8025     }
8026     
8027 });
8028
8029  
8030 /*
8031  * - LGPL
8032  *
8033  * Input
8034  * 
8035  */
8036
8037 /**
8038  * @class Roo.bootstrap.TextArea
8039  * @extends Roo.bootstrap.Input
8040  * Bootstrap TextArea class
8041  * @cfg {Number} cols Specifies the visible width of a text area
8042  * @cfg {Number} rows Specifies the visible number of lines in a text area
8043  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8044  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8045  * @cfg {string} html text
8046  * 
8047  * @constructor
8048  * Create a new TextArea
8049  * @param {Object} config The config object
8050  */
8051
8052 Roo.bootstrap.TextArea = function(config){
8053     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8054    
8055 };
8056
8057 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8058      
8059     cols : false,
8060     rows : 5,
8061     readOnly : false,
8062     warp : 'soft',
8063     resize : false,
8064     value: false,
8065     html: false,
8066     
8067     getAutoCreate : function(){
8068         
8069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8070         
8071         var id = Roo.id();
8072         
8073         var cfg = {};
8074         
8075         var input =  {
8076             tag: 'textarea',
8077             id : id,
8078             warp : this.warp,
8079             rows : this.rows,
8080             value : this.value || '',
8081             html: this.html || '',
8082             cls : 'form-control',
8083             placeholder : this.placeholder || '' 
8084             
8085         };
8086         
8087         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8088             input.maxLength = this.maxLength;
8089         }
8090         
8091         if(this.resize){
8092             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8093         }
8094         
8095         if(this.cols){
8096             input.cols = this.cols;
8097         }
8098         
8099         if (this.readOnly) {
8100             input.readonly = true;
8101         }
8102         
8103         if (this.name) {
8104             input.name = this.name;
8105         }
8106         
8107         if (this.size) {
8108             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8109         }
8110         
8111         var settings=this;
8112         ['xs','sm','md','lg'].map(function(size){
8113             if (settings[size]) {
8114                 cfg.cls += ' col-' + size + '-' + settings[size];
8115             }
8116         });
8117         
8118         var inputblock = input;
8119         
8120         if(this.hasFeedback && !this.allowBlank){
8121             
8122             var feedback = {
8123                 tag: 'span',
8124                 cls: 'glyphicon form-control-feedback'
8125             };
8126
8127             inputblock = {
8128                 cls : 'has-feedback',
8129                 cn :  [
8130                     input,
8131                     feedback
8132                 ] 
8133             };  
8134         }
8135         
8136         
8137         if (this.before || this.after) {
8138             
8139             inputblock = {
8140                 cls : 'input-group',
8141                 cn :  [] 
8142             };
8143             if (this.before) {
8144                 inputblock.cn.push({
8145                     tag :'span',
8146                     cls : 'input-group-addon',
8147                     html : this.before
8148                 });
8149             }
8150             
8151             inputblock.cn.push(input);
8152             
8153             if(this.hasFeedback && !this.allowBlank){
8154                 inputblock.cls += ' has-feedback';
8155                 inputblock.cn.push(feedback);
8156             }
8157             
8158             if (this.after) {
8159                 inputblock.cn.push({
8160                     tag :'span',
8161                     cls : 'input-group-addon',
8162                     html : this.after
8163                 });
8164             }
8165             
8166         }
8167         
8168         if (align ==='left' && this.fieldLabel.length) {
8169                 Roo.log("left and has label");
8170                 cfg.cn = [
8171                     
8172                     {
8173                         tag: 'label',
8174                         'for' :  id,
8175                         cls : 'control-label col-sm-' + this.labelWidth,
8176                         html : this.fieldLabel
8177                         
8178                     },
8179                     {
8180                         cls : "col-sm-" + (12 - this.labelWidth), 
8181                         cn: [
8182                             inputblock
8183                         ]
8184                     }
8185                     
8186                 ];
8187         } else if ( this.fieldLabel.length) {
8188                 Roo.log(" label");
8189                  cfg.cn = [
8190                    
8191                     {
8192                         tag: 'label',
8193                         //cls : 'input-group-addon',
8194                         html : this.fieldLabel
8195                         
8196                     },
8197                     
8198                     inputblock
8199                     
8200                 ];
8201
8202         } else {
8203             
8204                    Roo.log(" no label && no align");
8205                 cfg.cn = [
8206                     
8207                         inputblock
8208                     
8209                 ];
8210                 
8211                 
8212         }
8213         
8214         if (this.disabled) {
8215             input.disabled=true;
8216         }
8217         
8218         return cfg;
8219         
8220     },
8221     /**
8222      * return the real textarea element.
8223      */
8224     inputEl: function ()
8225     {
8226         return this.el.select('textarea.form-control',true).first();
8227     }
8228 });
8229
8230  
8231 /*
8232  * - LGPL
8233  *
8234  * trigger field - base class for combo..
8235  * 
8236  */
8237  
8238 /**
8239  * @class Roo.bootstrap.TriggerField
8240  * @extends Roo.bootstrap.Input
8241  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8242  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8243  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8244  * for which you can provide a custom implementation.  For example:
8245  * <pre><code>
8246 var trigger = new Roo.bootstrap.TriggerField();
8247 trigger.onTriggerClick = myTriggerFn;
8248 trigger.applyTo('my-field');
8249 </code></pre>
8250  *
8251  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8252  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8253  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8254  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8255  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8256
8257  * @constructor
8258  * Create a new TriggerField.
8259  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8260  * to the base TextField)
8261  */
8262 Roo.bootstrap.TriggerField = function(config){
8263     this.mimicing = false;
8264     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8265 };
8266
8267 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8268     /**
8269      * @cfg {String} triggerClass A CSS class to apply to the trigger
8270      */
8271      /**
8272      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8273      */
8274     hideTrigger:false,
8275
8276     /** @cfg {Boolean} grow @hide */
8277     /** @cfg {Number} growMin @hide */
8278     /** @cfg {Number} growMax @hide */
8279
8280     /**
8281      * @hide 
8282      * @method
8283      */
8284     autoSize: Roo.emptyFn,
8285     // private
8286     monitorTab : true,
8287     // private
8288     deferHeight : true,
8289
8290     
8291     actionMode : 'wrap',
8292     
8293     caret : false,
8294     
8295     
8296     getAutoCreate : function(){
8297        
8298         var align = this.labelAlign || this.parentLabelAlign();
8299         
8300         var id = Roo.id();
8301         
8302         var cfg = {
8303             cls: 'form-group' //input-group
8304         };
8305         
8306         
8307         var input =  {
8308             tag: 'input',
8309             id : id,
8310             type : this.inputType,
8311             cls : 'form-control',
8312             autocomplete: 'new-password',
8313             placeholder : this.placeholder || '' 
8314             
8315         };
8316         if (this.name) {
8317             input.name = this.name;
8318         }
8319         if (this.size) {
8320             input.cls += ' input-' + this.size;
8321         }
8322         
8323         if (this.disabled) {
8324             input.disabled=true;
8325         }
8326         
8327         var inputblock = input;
8328         
8329         if(this.hasFeedback && !this.allowBlank){
8330             
8331             var feedback = {
8332                 tag: 'span',
8333                 cls: 'glyphicon form-control-feedback'
8334             };
8335
8336             inputblock = {
8337                 cls : 'has-feedback',
8338                 cn :  [
8339                     input,
8340                     feedback
8341                 ] 
8342             };  
8343         }
8344         
8345         if (this.before || this.after) {
8346             
8347             inputblock = {
8348                 cls : 'input-group',
8349                 cn :  [] 
8350             };
8351             if (this.before) {
8352                 inputblock.cn.push({
8353                     tag :'span',
8354                     cls : 'input-group-addon',
8355                     html : this.before
8356                 });
8357             }
8358             
8359             inputblock.cn.push(input);
8360             
8361             if(this.hasFeedback && !this.allowBlank){
8362                 inputblock.cls += ' has-feedback';
8363                 inputblock.cn.push(feedback);
8364             }
8365             
8366             if (this.after) {
8367                 inputblock.cn.push({
8368                     tag :'span',
8369                     cls : 'input-group-addon',
8370                     html : this.after
8371                 });
8372             }
8373             
8374         };
8375         
8376         var box = {
8377             tag: 'div',
8378             cn: [
8379                 {
8380                     tag: 'input',
8381                     type : 'hidden',
8382                     cls: 'form-hidden-field'
8383                 },
8384                 inputblock
8385             ]
8386             
8387         };
8388         
8389         if(this.multiple){
8390             Roo.log('multiple');
8391             
8392             box = {
8393                 tag: 'div',
8394                 cn: [
8395                     {
8396                         tag: 'input',
8397                         type : 'hidden',
8398                         cls: 'form-hidden-field'
8399                     },
8400                     {
8401                         tag: 'ul',
8402                         cls: 'select2-choices',
8403                         cn:[
8404                             {
8405                                 tag: 'li',
8406                                 cls: 'select2-search-field',
8407                                 cn: [
8408
8409                                     inputblock
8410                                 ]
8411                             }
8412                         ]
8413                     }
8414                 ]
8415             }
8416         };
8417         
8418         var combobox = {
8419             cls: 'select2-container input-group',
8420             cn: [
8421                 box
8422 //                {
8423 //                    tag: 'ul',
8424 //                    cls: 'typeahead typeahead-long dropdown-menu',
8425 //                    style: 'display:none'
8426 //                }
8427             ]
8428         };
8429         
8430         if(!this.multiple && this.showToggleBtn){
8431             
8432             var caret = {
8433                         tag: 'span',
8434                         cls: 'caret'
8435              };
8436             if (this.caret != false) {
8437                 caret = {
8438                      tag: 'i',
8439                      cls: 'fa fa-' + this.caret
8440                 };
8441                 
8442             }
8443             
8444             combobox.cn.push({
8445                 tag :'span',
8446                 cls : 'input-group-addon btn dropdown-toggle',
8447                 cn : [
8448                     caret,
8449                     {
8450                         tag: 'span',
8451                         cls: 'combobox-clear',
8452                         cn  : [
8453                             {
8454                                 tag : 'i',
8455                                 cls: 'icon-remove'
8456                             }
8457                         ]
8458                     }
8459                 ]
8460
8461             })
8462         }
8463         
8464         if(this.multiple){
8465             combobox.cls += ' select2-container-multi';
8466         }
8467         
8468         if (align ==='left' && this.fieldLabel.length) {
8469             
8470                 Roo.log("left and has label");
8471                 cfg.cn = [
8472                     
8473                     {
8474                         tag: 'label',
8475                         'for' :  id,
8476                         cls : 'control-label col-sm-' + this.labelWidth,
8477                         html : this.fieldLabel
8478                         
8479                     },
8480                     {
8481                         cls : "col-sm-" + (12 - this.labelWidth), 
8482                         cn: [
8483                             combobox
8484                         ]
8485                     }
8486                     
8487                 ];
8488         } else if ( this.fieldLabel.length) {
8489                 Roo.log(" label");
8490                  cfg.cn = [
8491                    
8492                     {
8493                         tag: 'label',
8494                         //cls : 'input-group-addon',
8495                         html : this.fieldLabel
8496                         
8497                     },
8498                     
8499                     combobox
8500                     
8501                 ];
8502
8503         } else {
8504             
8505                 Roo.log(" no label && no align");
8506                 cfg = combobox
8507                      
8508                 
8509         }
8510          
8511         var settings=this;
8512         ['xs','sm','md','lg'].map(function(size){
8513             if (settings[size]) {
8514                 cfg.cls += ' col-' + size + '-' + settings[size];
8515             }
8516         });
8517         
8518         return cfg;
8519         
8520     },
8521     
8522     
8523     
8524     // private
8525     onResize : function(w, h){
8526 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8527 //        if(typeof w == 'number'){
8528 //            var x = w - this.trigger.getWidth();
8529 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8530 //            this.trigger.setStyle('left', x+'px');
8531 //        }
8532     },
8533
8534     // private
8535     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8536
8537     // private
8538     getResizeEl : function(){
8539         return this.inputEl();
8540     },
8541
8542     // private
8543     getPositionEl : function(){
8544         return this.inputEl();
8545     },
8546
8547     // private
8548     alignErrorIcon : function(){
8549         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8550     },
8551
8552     // private
8553     initEvents : function(){
8554         
8555         this.createList();
8556         
8557         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8558         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8559         if(!this.multiple && this.showToggleBtn){
8560             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8561             if(this.hideTrigger){
8562                 this.trigger.setDisplayed(false);
8563             }
8564             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8565         }
8566         
8567         if(this.multiple){
8568             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8569         }
8570         
8571         //this.trigger.addClassOnOver('x-form-trigger-over');
8572         //this.trigger.addClassOnClick('x-form-trigger-click');
8573         
8574         //if(!this.width){
8575         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8576         //}
8577     },
8578     
8579     createList : function()
8580     {
8581         this.list = Roo.get(document.body).createChild({
8582             tag: 'ul',
8583             cls: 'typeahead typeahead-long dropdown-menu',
8584             style: 'display:none'
8585         });
8586         
8587         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8588         
8589     },
8590
8591     // private
8592     initTrigger : function(){
8593        
8594     },
8595
8596     // private
8597     onDestroy : function(){
8598         if(this.trigger){
8599             this.trigger.removeAllListeners();
8600           //  this.trigger.remove();
8601         }
8602         //if(this.wrap){
8603         //    this.wrap.remove();
8604         //}
8605         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8606     },
8607
8608     // private
8609     onFocus : function(){
8610         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8611         /*
8612         if(!this.mimicing){
8613             this.wrap.addClass('x-trigger-wrap-focus');
8614             this.mimicing = true;
8615             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8616             if(this.monitorTab){
8617                 this.el.on("keydown", this.checkTab, this);
8618             }
8619         }
8620         */
8621     },
8622
8623     // private
8624     checkTab : function(e){
8625         if(e.getKey() == e.TAB){
8626             this.triggerBlur();
8627         }
8628     },
8629
8630     // private
8631     onBlur : function(){
8632         // do nothing
8633     },
8634
8635     // private
8636     mimicBlur : function(e, t){
8637         /*
8638         if(!this.wrap.contains(t) && this.validateBlur()){
8639             this.triggerBlur();
8640         }
8641         */
8642     },
8643
8644     // private
8645     triggerBlur : function(){
8646         this.mimicing = false;
8647         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8648         if(this.monitorTab){
8649             this.el.un("keydown", this.checkTab, this);
8650         }
8651         //this.wrap.removeClass('x-trigger-wrap-focus');
8652         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8653     },
8654
8655     // private
8656     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8657     validateBlur : function(e, t){
8658         return true;
8659     },
8660
8661     // private
8662     onDisable : function(){
8663         this.inputEl().dom.disabled = true;
8664         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8665         //if(this.wrap){
8666         //    this.wrap.addClass('x-item-disabled');
8667         //}
8668     },
8669
8670     // private
8671     onEnable : function(){
8672         this.inputEl().dom.disabled = false;
8673         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8674         //if(this.wrap){
8675         //    this.el.removeClass('x-item-disabled');
8676         //}
8677     },
8678
8679     // private
8680     onShow : function(){
8681         var ae = this.getActionEl();
8682         
8683         if(ae){
8684             ae.dom.style.display = '';
8685             ae.dom.style.visibility = 'visible';
8686         }
8687     },
8688
8689     // private
8690     
8691     onHide : function(){
8692         var ae = this.getActionEl();
8693         ae.dom.style.display = 'none';
8694     },
8695
8696     /**
8697      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8698      * by an implementing function.
8699      * @method
8700      * @param {EventObject} e
8701      */
8702     onTriggerClick : Roo.emptyFn
8703 });
8704  /*
8705  * Based on:
8706  * Ext JS Library 1.1.1
8707  * Copyright(c) 2006-2007, Ext JS, LLC.
8708  *
8709  * Originally Released Under LGPL - original licence link has changed is not relivant.
8710  *
8711  * Fork - LGPL
8712  * <script type="text/javascript">
8713  */
8714
8715
8716 /**
8717  * @class Roo.data.SortTypes
8718  * @singleton
8719  * Defines the default sorting (casting?) comparison functions used when sorting data.
8720  */
8721 Roo.data.SortTypes = {
8722     /**
8723      * Default sort that does nothing
8724      * @param {Mixed} s The value being converted
8725      * @return {Mixed} The comparison value
8726      */
8727     none : function(s){
8728         return s;
8729     },
8730     
8731     /**
8732      * The regular expression used to strip tags
8733      * @type {RegExp}
8734      * @property
8735      */
8736     stripTagsRE : /<\/?[^>]+>/gi,
8737     
8738     /**
8739      * Strips all HTML tags to sort on text only
8740      * @param {Mixed} s The value being converted
8741      * @return {String} The comparison value
8742      */
8743     asText : function(s){
8744         return String(s).replace(this.stripTagsRE, "");
8745     },
8746     
8747     /**
8748      * Strips all HTML tags to sort on text only - Case insensitive
8749      * @param {Mixed} s The value being converted
8750      * @return {String} The comparison value
8751      */
8752     asUCText : function(s){
8753         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8754     },
8755     
8756     /**
8757      * Case insensitive string
8758      * @param {Mixed} s The value being converted
8759      * @return {String} The comparison value
8760      */
8761     asUCString : function(s) {
8762         return String(s).toUpperCase();
8763     },
8764     
8765     /**
8766      * Date sorting
8767      * @param {Mixed} s The value being converted
8768      * @return {Number} The comparison value
8769      */
8770     asDate : function(s) {
8771         if(!s){
8772             return 0;
8773         }
8774         if(s instanceof Date){
8775             return s.getTime();
8776         }
8777         return Date.parse(String(s));
8778     },
8779     
8780     /**
8781      * Float sorting
8782      * @param {Mixed} s The value being converted
8783      * @return {Float} The comparison value
8784      */
8785     asFloat : function(s) {
8786         var val = parseFloat(String(s).replace(/,/g, ""));
8787         if(isNaN(val)) val = 0;
8788         return val;
8789     },
8790     
8791     /**
8792      * Integer sorting
8793      * @param {Mixed} s The value being converted
8794      * @return {Number} The comparison value
8795      */
8796     asInt : function(s) {
8797         var val = parseInt(String(s).replace(/,/g, ""));
8798         if(isNaN(val)) val = 0;
8799         return val;
8800     }
8801 };/*
8802  * Based on:
8803  * Ext JS Library 1.1.1
8804  * Copyright(c) 2006-2007, Ext JS, LLC.
8805  *
8806  * Originally Released Under LGPL - original licence link has changed is not relivant.
8807  *
8808  * Fork - LGPL
8809  * <script type="text/javascript">
8810  */
8811
8812 /**
8813 * @class Roo.data.Record
8814  * Instances of this class encapsulate both record <em>definition</em> information, and record
8815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8816  * to access Records cached in an {@link Roo.data.Store} object.<br>
8817  * <p>
8818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8820  * objects.<br>
8821  * <p>
8822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8823  * @constructor
8824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8825  * {@link #create}. The parameters are the same.
8826  * @param {Array} data An associative Array of data values keyed by the field name.
8827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8829  * not specified an integer id is generated.
8830  */
8831 Roo.data.Record = function(data, id){
8832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8833     this.data = data;
8834 };
8835
8836 /**
8837  * Generate a constructor for a specific record layout.
8838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8840  * Each field definition object may contain the following properties: <ul>
8841  * <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,
8842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8848  * this may be omitted.</p></li>
8849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8850  * <ul><li>auto (Default, implies no conversion)</li>
8851  * <li>string</li>
8852  * <li>int</li>
8853  * <li>float</li>
8854  * <li>boolean</li>
8855  * <li>date</li></ul></p></li>
8856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8859  * by the Reader into an object that will be stored in the Record. It is passed the
8860  * following parameters:<ul>
8861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8862  * </ul></p></li>
8863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8864  * </ul>
8865  * <br>usage:<br><pre><code>
8866 var TopicRecord = Roo.data.Record.create(
8867     {name: 'title', mapping: 'topic_title'},
8868     {name: 'author', mapping: 'username'},
8869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8871     {name: 'lastPoster', mapping: 'user2'},
8872     {name: 'excerpt', mapping: 'post_text'}
8873 );
8874
8875 var myNewRecord = new TopicRecord({
8876     title: 'Do my job please',
8877     author: 'noobie',
8878     totalPosts: 1,
8879     lastPost: new Date(),
8880     lastPoster: 'Animal',
8881     excerpt: 'No way dude!'
8882 });
8883 myStore.add(myNewRecord);
8884 </code></pre>
8885  * @method create
8886  * @static
8887  */
8888 Roo.data.Record.create = function(o){
8889     var f = function(){
8890         f.superclass.constructor.apply(this, arguments);
8891     };
8892     Roo.extend(f, Roo.data.Record);
8893     var p = f.prototype;
8894     p.fields = new Roo.util.MixedCollection(false, function(field){
8895         return field.name;
8896     });
8897     for(var i = 0, len = o.length; i < len; i++){
8898         p.fields.add(new Roo.data.Field(o[i]));
8899     }
8900     f.getField = function(name){
8901         return p.fields.get(name);  
8902     };
8903     return f;
8904 };
8905
8906 Roo.data.Record.AUTO_ID = 1000;
8907 Roo.data.Record.EDIT = 'edit';
8908 Roo.data.Record.REJECT = 'reject';
8909 Roo.data.Record.COMMIT = 'commit';
8910
8911 Roo.data.Record.prototype = {
8912     /**
8913      * Readonly flag - true if this record has been modified.
8914      * @type Boolean
8915      */
8916     dirty : false,
8917     editing : false,
8918     error: null,
8919     modified: null,
8920
8921     // private
8922     join : function(store){
8923         this.store = store;
8924     },
8925
8926     /**
8927      * Set the named field to the specified value.
8928      * @param {String} name The name of the field to set.
8929      * @param {Object} value The value to set the field to.
8930      */
8931     set : function(name, value){
8932         if(this.data[name] == value){
8933             return;
8934         }
8935         this.dirty = true;
8936         if(!this.modified){
8937             this.modified = {};
8938         }
8939         if(typeof this.modified[name] == 'undefined'){
8940             this.modified[name] = this.data[name];
8941         }
8942         this.data[name] = value;
8943         if(!this.editing && this.store){
8944             this.store.afterEdit(this);
8945         }       
8946     },
8947
8948     /**
8949      * Get the value of the named field.
8950      * @param {String} name The name of the field to get the value of.
8951      * @return {Object} The value of the field.
8952      */
8953     get : function(name){
8954         return this.data[name]; 
8955     },
8956
8957     // private
8958     beginEdit : function(){
8959         this.editing = true;
8960         this.modified = {}; 
8961     },
8962
8963     // private
8964     cancelEdit : function(){
8965         this.editing = false;
8966         delete this.modified;
8967     },
8968
8969     // private
8970     endEdit : function(){
8971         this.editing = false;
8972         if(this.dirty && this.store){
8973             this.store.afterEdit(this);
8974         }
8975     },
8976
8977     /**
8978      * Usually called by the {@link Roo.data.Store} which owns the Record.
8979      * Rejects all changes made to the Record since either creation, or the last commit operation.
8980      * Modified fields are reverted to their original values.
8981      * <p>
8982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8983      * of reject operations.
8984      */
8985     reject : function(){
8986         var m = this.modified;
8987         for(var n in m){
8988             if(typeof m[n] != "function"){
8989                 this.data[n] = m[n];
8990             }
8991         }
8992         this.dirty = false;
8993         delete this.modified;
8994         this.editing = false;
8995         if(this.store){
8996             this.store.afterReject(this);
8997         }
8998     },
8999
9000     /**
9001      * Usually called by the {@link Roo.data.Store} which owns the Record.
9002      * Commits all changes made to the Record since either creation, or the last commit operation.
9003      * <p>
9004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9005      * of commit operations.
9006      */
9007     commit : function(){
9008         this.dirty = false;
9009         delete this.modified;
9010         this.editing = false;
9011         if(this.store){
9012             this.store.afterCommit(this);
9013         }
9014     },
9015
9016     // private
9017     hasError : function(){
9018         return this.error != null;
9019     },
9020
9021     // private
9022     clearError : function(){
9023         this.error = null;
9024     },
9025
9026     /**
9027      * Creates a copy of this record.
9028      * @param {String} id (optional) A new record id if you don't want to use this record's id
9029      * @return {Record}
9030      */
9031     copy : function(newId) {
9032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9033     }
9034 };/*
9035  * Based on:
9036  * Ext JS Library 1.1.1
9037  * Copyright(c) 2006-2007, Ext JS, LLC.
9038  *
9039  * Originally Released Under LGPL - original licence link has changed is not relivant.
9040  *
9041  * Fork - LGPL
9042  * <script type="text/javascript">
9043  */
9044
9045
9046
9047 /**
9048  * @class Roo.data.Store
9049  * @extends Roo.util.Observable
9050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9052  * <p>
9053  * 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
9054  * has no knowledge of the format of the data returned by the Proxy.<br>
9055  * <p>
9056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9057  * instances from the data object. These records are cached and made available through accessor functions.
9058  * @constructor
9059  * Creates a new Store.
9060  * @param {Object} config A config object containing the objects needed for the Store to access data,
9061  * and read the data into Records.
9062  */
9063 Roo.data.Store = function(config){
9064     this.data = new Roo.util.MixedCollection(false);
9065     this.data.getKey = function(o){
9066         return o.id;
9067     };
9068     this.baseParams = {};
9069     // private
9070     this.paramNames = {
9071         "start" : "start",
9072         "limit" : "limit",
9073         "sort" : "sort",
9074         "dir" : "dir",
9075         "multisort" : "_multisort"
9076     };
9077
9078     if(config && config.data){
9079         this.inlineData = config.data;
9080         delete config.data;
9081     }
9082
9083     Roo.apply(this, config);
9084     
9085     if(this.reader){ // reader passed
9086         this.reader = Roo.factory(this.reader, Roo.data);
9087         this.reader.xmodule = this.xmodule || false;
9088         if(!this.recordType){
9089             this.recordType = this.reader.recordType;
9090         }
9091         if(this.reader.onMetaChange){
9092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9093         }
9094     }
9095
9096     if(this.recordType){
9097         this.fields = this.recordType.prototype.fields;
9098     }
9099     this.modified = [];
9100
9101     this.addEvents({
9102         /**
9103          * @event datachanged
9104          * Fires when the data cache has changed, and a widget which is using this Store
9105          * as a Record cache should refresh its view.
9106          * @param {Store} this
9107          */
9108         datachanged : true,
9109         /**
9110          * @event metachange
9111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9112          * @param {Store} this
9113          * @param {Object} meta The JSON metadata
9114          */
9115         metachange : true,
9116         /**
9117          * @event add
9118          * Fires when Records have been added to the Store
9119          * @param {Store} this
9120          * @param {Roo.data.Record[]} records The array of Records added
9121          * @param {Number} index The index at which the record(s) were added
9122          */
9123         add : true,
9124         /**
9125          * @event remove
9126          * Fires when a Record has been removed from the Store
9127          * @param {Store} this
9128          * @param {Roo.data.Record} record The Record that was removed
9129          * @param {Number} index The index at which the record was removed
9130          */
9131         remove : true,
9132         /**
9133          * @event update
9134          * Fires when a Record has been updated
9135          * @param {Store} this
9136          * @param {Roo.data.Record} record The Record that was updated
9137          * @param {String} operation The update operation being performed.  Value may be one of:
9138          * <pre><code>
9139  Roo.data.Record.EDIT
9140  Roo.data.Record.REJECT
9141  Roo.data.Record.COMMIT
9142          * </code></pre>
9143          */
9144         update : true,
9145         /**
9146          * @event clear
9147          * Fires when the data cache has been cleared.
9148          * @param {Store} this
9149          */
9150         clear : true,
9151         /**
9152          * @event beforeload
9153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9154          * the load action will be canceled.
9155          * @param {Store} this
9156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9157          */
9158         beforeload : true,
9159         /**
9160          * @event beforeloadadd
9161          * Fires after a new set of Records has been loaded.
9162          * @param {Store} this
9163          * @param {Roo.data.Record[]} records The Records that were loaded
9164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9165          */
9166         beforeloadadd : true,
9167         /**
9168          * @event load
9169          * Fires after a new set of Records has been loaded, before they are added to the store.
9170          * @param {Store} this
9171          * @param {Roo.data.Record[]} records The Records that were loaded
9172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9173          * @params {Object} return from reader
9174          */
9175         load : true,
9176         /**
9177          * @event loadexception
9178          * Fires if an exception occurs in the Proxy during loading.
9179          * Called with the signature of the Proxy's "loadexception" event.
9180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9181          * 
9182          * @param {Proxy} 
9183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9184          * @param {Object} load options 
9185          * @param {Object} jsonData from your request (normally this contains the Exception)
9186          */
9187         loadexception : true
9188     });
9189     
9190     if(this.proxy){
9191         this.proxy = Roo.factory(this.proxy, Roo.data);
9192         this.proxy.xmodule = this.xmodule || false;
9193         this.relayEvents(this.proxy,  ["loadexception"]);
9194     }
9195     this.sortToggle = {};
9196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9197
9198     Roo.data.Store.superclass.constructor.call(this);
9199
9200     if(this.inlineData){
9201         this.loadData(this.inlineData);
9202         delete this.inlineData;
9203     }
9204 };
9205
9206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9207      /**
9208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9209     * without a remote query - used by combo/forms at present.
9210     */
9211     
9212     /**
9213     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9214     */
9215     /**
9216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9217     */
9218     /**
9219     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9221     */
9222     /**
9223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9224     * on any HTTP request
9225     */
9226     /**
9227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9228     */
9229     /**
9230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9231     */
9232     multiSort: false,
9233     /**
9234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9236     */
9237     remoteSort : false,
9238
9239     /**
9240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9241      * loaded or when a record is removed. (defaults to false).
9242     */
9243     pruneModifiedRecords : false,
9244
9245     // private
9246     lastOptions : null,
9247
9248     /**
9249      * Add Records to the Store and fires the add event.
9250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9251      */
9252     add : function(records){
9253         records = [].concat(records);
9254         for(var i = 0, len = records.length; i < len; i++){
9255             records[i].join(this);
9256         }
9257         var index = this.data.length;
9258         this.data.addAll(records);
9259         this.fireEvent("add", this, records, index);
9260     },
9261
9262     /**
9263      * Remove a Record from the Store and fires the remove event.
9264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9265      */
9266     remove : function(record){
9267         var index = this.data.indexOf(record);
9268         this.data.removeAt(index);
9269         if(this.pruneModifiedRecords){
9270             this.modified.remove(record);
9271         }
9272         this.fireEvent("remove", this, record, index);
9273     },
9274
9275     /**
9276      * Remove all Records from the Store and fires the clear event.
9277      */
9278     removeAll : function(){
9279         this.data.clear();
9280         if(this.pruneModifiedRecords){
9281             this.modified = [];
9282         }
9283         this.fireEvent("clear", this);
9284     },
9285
9286     /**
9287      * Inserts Records to the Store at the given index and fires the add event.
9288      * @param {Number} index The start index at which to insert the passed Records.
9289      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9290      */
9291     insert : function(index, records){
9292         records = [].concat(records);
9293         for(var i = 0, len = records.length; i < len; i++){
9294             this.data.insert(index, records[i]);
9295             records[i].join(this);
9296         }
9297         this.fireEvent("add", this, records, index);
9298     },
9299
9300     /**
9301      * Get the index within the cache of the passed Record.
9302      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9303      * @return {Number} The index of the passed Record. Returns -1 if not found.
9304      */
9305     indexOf : function(record){
9306         return this.data.indexOf(record);
9307     },
9308
9309     /**
9310      * Get the index within the cache of the Record with the passed id.
9311      * @param {String} id The id of the Record to find.
9312      * @return {Number} The index of the Record. Returns -1 if not found.
9313      */
9314     indexOfId : function(id){
9315         return this.data.indexOfKey(id);
9316     },
9317
9318     /**
9319      * Get the Record with the specified id.
9320      * @param {String} id The id of the Record to find.
9321      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9322      */
9323     getById : function(id){
9324         return this.data.key(id);
9325     },
9326
9327     /**
9328      * Get the Record at the specified index.
9329      * @param {Number} index The index of the Record to find.
9330      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9331      */
9332     getAt : function(index){
9333         return this.data.itemAt(index);
9334     },
9335
9336     /**
9337      * Returns a range of Records between specified indices.
9338      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9339      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9340      * @return {Roo.data.Record[]} An array of Records
9341      */
9342     getRange : function(start, end){
9343         return this.data.getRange(start, end);
9344     },
9345
9346     // private
9347     storeOptions : function(o){
9348         o = Roo.apply({}, o);
9349         delete o.callback;
9350         delete o.scope;
9351         this.lastOptions = o;
9352     },
9353
9354     /**
9355      * Loads the Record cache from the configured Proxy using the configured Reader.
9356      * <p>
9357      * If using remote paging, then the first load call must specify the <em>start</em>
9358      * and <em>limit</em> properties in the options.params property to establish the initial
9359      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9360      * <p>
9361      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9362      * and this call will return before the new data has been loaded. Perform any post-processing
9363      * in a callback function, or in a "load" event handler.</strong>
9364      * <p>
9365      * @param {Object} options An object containing properties which control loading options:<ul>
9366      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9367      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9368      * passed the following arguments:<ul>
9369      * <li>r : Roo.data.Record[]</li>
9370      * <li>options: Options object from the load call</li>
9371      * <li>success: Boolean success indicator</li></ul></li>
9372      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9373      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9374      * </ul>
9375      */
9376     load : function(options){
9377         options = options || {};
9378         if(this.fireEvent("beforeload", this, options) !== false){
9379             this.storeOptions(options);
9380             var p = Roo.apply(options.params || {}, this.baseParams);
9381             // if meta was not loaded from remote source.. try requesting it.
9382             if (!this.reader.metaFromRemote) {
9383                 p._requestMeta = 1;
9384             }
9385             if(this.sortInfo && this.remoteSort){
9386                 var pn = this.paramNames;
9387                 p[pn["sort"]] = this.sortInfo.field;
9388                 p[pn["dir"]] = this.sortInfo.direction;
9389             }
9390             if (this.multiSort) {
9391                 var pn = this.paramNames;
9392                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9393             }
9394             
9395             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9396         }
9397     },
9398
9399     /**
9400      * Reloads the Record cache from the configured Proxy using the configured Reader and
9401      * the options from the last load operation performed.
9402      * @param {Object} options (optional) An object containing properties which may override the options
9403      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9404      * the most recently used options are reused).
9405      */
9406     reload : function(options){
9407         this.load(Roo.applyIf(options||{}, this.lastOptions));
9408     },
9409
9410     // private
9411     // Called as a callback by the Reader during a load operation.
9412     loadRecords : function(o, options, success){
9413         if(!o || success === false){
9414             if(success !== false){
9415                 this.fireEvent("load", this, [], options, o);
9416             }
9417             if(options.callback){
9418                 options.callback.call(options.scope || this, [], options, false);
9419             }
9420             return;
9421         }
9422         // if data returned failure - throw an exception.
9423         if (o.success === false) {
9424             // show a message if no listener is registered.
9425             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9426                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9427             }
9428             // loadmask wil be hooked into this..
9429             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9430             return;
9431         }
9432         var r = o.records, t = o.totalRecords || r.length;
9433         
9434         this.fireEvent("beforeloadadd", this, r, options, o);
9435         
9436         if(!options || options.add !== true){
9437             if(this.pruneModifiedRecords){
9438                 this.modified = [];
9439             }
9440             for(var i = 0, len = r.length; i < len; i++){
9441                 r[i].join(this);
9442             }
9443             if(this.snapshot){
9444                 this.data = this.snapshot;
9445                 delete this.snapshot;
9446             }
9447             this.data.clear();
9448             this.data.addAll(r);
9449             this.totalLength = t;
9450             this.applySort();
9451             this.fireEvent("datachanged", this);
9452         }else{
9453             this.totalLength = Math.max(t, this.data.length+r.length);
9454             this.add(r);
9455         }
9456         this.fireEvent("load", this, r, options, o);
9457         if(options.callback){
9458             options.callback.call(options.scope || this, r, options, true);
9459         }
9460     },
9461
9462
9463     /**
9464      * Loads data from a passed data block. A Reader which understands the format of the data
9465      * must have been configured in the constructor.
9466      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9467      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9468      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9469      */
9470     loadData : function(o, append){
9471         var r = this.reader.readRecords(o);
9472         this.loadRecords(r, {add: append}, true);
9473     },
9474
9475     /**
9476      * Gets the number of cached records.
9477      * <p>
9478      * <em>If using paging, this may not be the total size of the dataset. If the data object
9479      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9480      * the data set size</em>
9481      */
9482     getCount : function(){
9483         return this.data.length || 0;
9484     },
9485
9486     /**
9487      * Gets the total number of records in the dataset as returned by the server.
9488      * <p>
9489      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9490      * the dataset size</em>
9491      */
9492     getTotalCount : function(){
9493         return this.totalLength || 0;
9494     },
9495
9496     /**
9497      * Returns the sort state of the Store as an object with two properties:
9498      * <pre><code>
9499  field {String} The name of the field by which the Records are sorted
9500  direction {String} The sort order, "ASC" or "DESC"
9501      * </code></pre>
9502      */
9503     getSortState : function(){
9504         return this.sortInfo;
9505     },
9506
9507     // private
9508     applySort : function(){
9509         if(this.sortInfo && !this.remoteSort){
9510             var s = this.sortInfo, f = s.field;
9511             var st = this.fields.get(f).sortType;
9512             var fn = function(r1, r2){
9513                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9514                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9515             };
9516             this.data.sort(s.direction, fn);
9517             if(this.snapshot && this.snapshot != this.data){
9518                 this.snapshot.sort(s.direction, fn);
9519             }
9520         }
9521     },
9522
9523     /**
9524      * Sets the default sort column and order to be used by the next load operation.
9525      * @param {String} fieldName The name of the field to sort by.
9526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9527      */
9528     setDefaultSort : function(field, dir){
9529         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9530     },
9531
9532     /**
9533      * Sort the Records.
9534      * If remote sorting is used, the sort is performed on the server, and the cache is
9535      * reloaded. If local sorting is used, the cache is sorted internally.
9536      * @param {String} fieldName The name of the field to sort by.
9537      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9538      */
9539     sort : function(fieldName, dir){
9540         var f = this.fields.get(fieldName);
9541         if(!dir){
9542             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9543             
9544             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9545                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9546             }else{
9547                 dir = f.sortDir;
9548             }
9549         }
9550         this.sortToggle[f.name] = dir;
9551         this.sortInfo = {field: f.name, direction: dir};
9552         if(!this.remoteSort){
9553             this.applySort();
9554             this.fireEvent("datachanged", this);
9555         }else{
9556             this.load(this.lastOptions);
9557         }
9558     },
9559
9560     /**
9561      * Calls the specified function for each of the Records in the cache.
9562      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9563      * Returning <em>false</em> aborts and exits the iteration.
9564      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9565      */
9566     each : function(fn, scope){
9567         this.data.each(fn, scope);
9568     },
9569
9570     /**
9571      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9572      * (e.g., during paging).
9573      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9574      */
9575     getModifiedRecords : function(){
9576         return this.modified;
9577     },
9578
9579     // private
9580     createFilterFn : function(property, value, anyMatch){
9581         if(!value.exec){ // not a regex
9582             value = String(value);
9583             if(value.length == 0){
9584                 return false;
9585             }
9586             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9587         }
9588         return function(r){
9589             return value.test(r.data[property]);
9590         };
9591     },
9592
9593     /**
9594      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9595      * @param {String} property A field on your records
9596      * @param {Number} start The record index to start at (defaults to 0)
9597      * @param {Number} end The last record index to include (defaults to length - 1)
9598      * @return {Number} The sum
9599      */
9600     sum : function(property, start, end){
9601         var rs = this.data.items, v = 0;
9602         start = start || 0;
9603         end = (end || end === 0) ? end : rs.length-1;
9604
9605         for(var i = start; i <= end; i++){
9606             v += (rs[i].data[property] || 0);
9607         }
9608         return v;
9609     },
9610
9611     /**
9612      * Filter the records by a specified property.
9613      * @param {String} field A field on your records
9614      * @param {String/RegExp} value Either a string that the field
9615      * should start with or a RegExp to test against the field
9616      * @param {Boolean} anyMatch True to match any part not just the beginning
9617      */
9618     filter : function(property, value, anyMatch){
9619         var fn = this.createFilterFn(property, value, anyMatch);
9620         return fn ? this.filterBy(fn) : this.clearFilter();
9621     },
9622
9623     /**
9624      * Filter by a function. The specified function will be called with each
9625      * record in this data source. If the function returns true the record is included,
9626      * otherwise it is filtered.
9627      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9628      * @param {Object} scope (optional) The scope of the function (defaults to this)
9629      */
9630     filterBy : function(fn, scope){
9631         this.snapshot = this.snapshot || this.data;
9632         this.data = this.queryBy(fn, scope||this);
9633         this.fireEvent("datachanged", this);
9634     },
9635
9636     /**
9637      * Query the records by a specified property.
9638      * @param {String} field A field on your records
9639      * @param {String/RegExp} value Either a string that the field
9640      * should start with or a RegExp to test against the field
9641      * @param {Boolean} anyMatch True to match any part not just the beginning
9642      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9643      */
9644     query : function(property, value, anyMatch){
9645         var fn = this.createFilterFn(property, value, anyMatch);
9646         return fn ? this.queryBy(fn) : this.data.clone();
9647     },
9648
9649     /**
9650      * Query by a function. The specified function will be called with each
9651      * record in this data source. If the function returns true the record is included
9652      * in the results.
9653      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9654      * @param {Object} scope (optional) The scope of the function (defaults to this)
9655       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9656      **/
9657     queryBy : function(fn, scope){
9658         var data = this.snapshot || this.data;
9659         return data.filterBy(fn, scope||this);
9660     },
9661
9662     /**
9663      * Collects unique values for a particular dataIndex from this store.
9664      * @param {String} dataIndex The property to collect
9665      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9666      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9667      * @return {Array} An array of the unique values
9668      **/
9669     collect : function(dataIndex, allowNull, bypassFilter){
9670         var d = (bypassFilter === true && this.snapshot) ?
9671                 this.snapshot.items : this.data.items;
9672         var v, sv, r = [], l = {};
9673         for(var i = 0, len = d.length; i < len; i++){
9674             v = d[i].data[dataIndex];
9675             sv = String(v);
9676             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9677                 l[sv] = true;
9678                 r[r.length] = v;
9679             }
9680         }
9681         return r;
9682     },
9683
9684     /**
9685      * Revert to a view of the Record cache with no filtering applied.
9686      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9687      */
9688     clearFilter : function(suppressEvent){
9689         if(this.snapshot && this.snapshot != this.data){
9690             this.data = this.snapshot;
9691             delete this.snapshot;
9692             if(suppressEvent !== true){
9693                 this.fireEvent("datachanged", this);
9694             }
9695         }
9696     },
9697
9698     // private
9699     afterEdit : function(record){
9700         if(this.modified.indexOf(record) == -1){
9701             this.modified.push(record);
9702         }
9703         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9704     },
9705     
9706     // private
9707     afterReject : function(record){
9708         this.modified.remove(record);
9709         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9710     },
9711
9712     // private
9713     afterCommit : function(record){
9714         this.modified.remove(record);
9715         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9716     },
9717
9718     /**
9719      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9720      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9721      */
9722     commitChanges : function(){
9723         var m = this.modified.slice(0);
9724         this.modified = [];
9725         for(var i = 0, len = m.length; i < len; i++){
9726             m[i].commit();
9727         }
9728     },
9729
9730     /**
9731      * Cancel outstanding changes on all changed records.
9732      */
9733     rejectChanges : function(){
9734         var m = this.modified.slice(0);
9735         this.modified = [];
9736         for(var i = 0, len = m.length; i < len; i++){
9737             m[i].reject();
9738         }
9739     },
9740
9741     onMetaChange : function(meta, rtype, o){
9742         this.recordType = rtype;
9743         this.fields = rtype.prototype.fields;
9744         delete this.snapshot;
9745         this.sortInfo = meta.sortInfo || this.sortInfo;
9746         this.modified = [];
9747         this.fireEvent('metachange', this, this.reader.meta);
9748     },
9749     
9750     moveIndex : function(data, type)
9751     {
9752         var index = this.indexOf(data);
9753         
9754         var newIndex = index + type;
9755         
9756         this.remove(data);
9757         
9758         this.insert(newIndex, data);
9759         
9760     }
9761 });/*
9762  * Based on:
9763  * Ext JS Library 1.1.1
9764  * Copyright(c) 2006-2007, Ext JS, LLC.
9765  *
9766  * Originally Released Under LGPL - original licence link has changed is not relivant.
9767  *
9768  * Fork - LGPL
9769  * <script type="text/javascript">
9770  */
9771
9772 /**
9773  * @class Roo.data.SimpleStore
9774  * @extends Roo.data.Store
9775  * Small helper class to make creating Stores from Array data easier.
9776  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9777  * @cfg {Array} fields An array of field definition objects, or field name strings.
9778  * @cfg {Array} data The multi-dimensional array of data
9779  * @constructor
9780  * @param {Object} config
9781  */
9782 Roo.data.SimpleStore = function(config){
9783     Roo.data.SimpleStore.superclass.constructor.call(this, {
9784         isLocal : true,
9785         reader: new Roo.data.ArrayReader({
9786                 id: config.id
9787             },
9788             Roo.data.Record.create(config.fields)
9789         ),
9790         proxy : new Roo.data.MemoryProxy(config.data)
9791     });
9792     this.load();
9793 };
9794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9795  * Based on:
9796  * Ext JS Library 1.1.1
9797  * Copyright(c) 2006-2007, Ext JS, LLC.
9798  *
9799  * Originally Released Under LGPL - original licence link has changed is not relivant.
9800  *
9801  * Fork - LGPL
9802  * <script type="text/javascript">
9803  */
9804
9805 /**
9806 /**
9807  * @extends Roo.data.Store
9808  * @class Roo.data.JsonStore
9809  * Small helper class to make creating Stores for JSON data easier. <br/>
9810 <pre><code>
9811 var store = new Roo.data.JsonStore({
9812     url: 'get-images.php',
9813     root: 'images',
9814     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9815 });
9816 </code></pre>
9817  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9818  * JsonReader and HttpProxy (unless inline data is provided).</b>
9819  * @cfg {Array} fields An array of field definition objects, or field name strings.
9820  * @constructor
9821  * @param {Object} config
9822  */
9823 Roo.data.JsonStore = function(c){
9824     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9825         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9826         reader: new Roo.data.JsonReader(c, c.fields)
9827     }));
9828 };
9829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9830  * Based on:
9831  * Ext JS Library 1.1.1
9832  * Copyright(c) 2006-2007, Ext JS, LLC.
9833  *
9834  * Originally Released Under LGPL - original licence link has changed is not relivant.
9835  *
9836  * Fork - LGPL
9837  * <script type="text/javascript">
9838  */
9839
9840  
9841 Roo.data.Field = function(config){
9842     if(typeof config == "string"){
9843         config = {name: config};
9844     }
9845     Roo.apply(this, config);
9846     
9847     if(!this.type){
9848         this.type = "auto";
9849     }
9850     
9851     var st = Roo.data.SortTypes;
9852     // named sortTypes are supported, here we look them up
9853     if(typeof this.sortType == "string"){
9854         this.sortType = st[this.sortType];
9855     }
9856     
9857     // set default sortType for strings and dates
9858     if(!this.sortType){
9859         switch(this.type){
9860             case "string":
9861                 this.sortType = st.asUCString;
9862                 break;
9863             case "date":
9864                 this.sortType = st.asDate;
9865                 break;
9866             default:
9867                 this.sortType = st.none;
9868         }
9869     }
9870
9871     // define once
9872     var stripRe = /[\$,%]/g;
9873
9874     // prebuilt conversion function for this field, instead of
9875     // switching every time we're reading a value
9876     if(!this.convert){
9877         var cv, dateFormat = this.dateFormat;
9878         switch(this.type){
9879             case "":
9880             case "auto":
9881             case undefined:
9882                 cv = function(v){ return v; };
9883                 break;
9884             case "string":
9885                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9886                 break;
9887             case "int":
9888                 cv = function(v){
9889                     return v !== undefined && v !== null && v !== '' ?
9890                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9891                     };
9892                 break;
9893             case "float":
9894                 cv = function(v){
9895                     return v !== undefined && v !== null && v !== '' ?
9896                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9897                     };
9898                 break;
9899             case "bool":
9900             case "boolean":
9901                 cv = function(v){ return v === true || v === "true" || v == 1; };
9902                 break;
9903             case "date":
9904                 cv = function(v){
9905                     if(!v){
9906                         return '';
9907                     }
9908                     if(v instanceof Date){
9909                         return v;
9910                     }
9911                     if(dateFormat){
9912                         if(dateFormat == "timestamp"){
9913                             return new Date(v*1000);
9914                         }
9915                         return Date.parseDate(v, dateFormat);
9916                     }
9917                     var parsed = Date.parse(v);
9918                     return parsed ? new Date(parsed) : null;
9919                 };
9920              break;
9921             
9922         }
9923         this.convert = cv;
9924     }
9925 };
9926
9927 Roo.data.Field.prototype = {
9928     dateFormat: null,
9929     defaultValue: "",
9930     mapping: null,
9931     sortType : null,
9932     sortDir : "ASC"
9933 };/*
9934  * Based on:
9935  * Ext JS Library 1.1.1
9936  * Copyright(c) 2006-2007, Ext JS, LLC.
9937  *
9938  * Originally Released Under LGPL - original licence link has changed is not relivant.
9939  *
9940  * Fork - LGPL
9941  * <script type="text/javascript">
9942  */
9943  
9944 // Base class for reading structured data from a data source.  This class is intended to be
9945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9946
9947 /**
9948  * @class Roo.data.DataReader
9949  * Base class for reading structured data from a data source.  This class is intended to be
9950  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9951  */
9952
9953 Roo.data.DataReader = function(meta, recordType){
9954     
9955     this.meta = meta;
9956     
9957     this.recordType = recordType instanceof Array ? 
9958         Roo.data.Record.create(recordType) : recordType;
9959 };
9960
9961 Roo.data.DataReader.prototype = {
9962      /**
9963      * Create an empty record
9964      * @param {Object} data (optional) - overlay some values
9965      * @return {Roo.data.Record} record created.
9966      */
9967     newRow :  function(d) {
9968         var da =  {};
9969         this.recordType.prototype.fields.each(function(c) {
9970             switch( c.type) {
9971                 case 'int' : da[c.name] = 0; break;
9972                 case 'date' : da[c.name] = new Date(); break;
9973                 case 'float' : da[c.name] = 0.0; break;
9974                 case 'boolean' : da[c.name] = false; break;
9975                 default : da[c.name] = ""; break;
9976             }
9977             
9978         });
9979         return new this.recordType(Roo.apply(da, d));
9980     }
9981     
9982 };/*
9983  * Based on:
9984  * Ext JS Library 1.1.1
9985  * Copyright(c) 2006-2007, Ext JS, LLC.
9986  *
9987  * Originally Released Under LGPL - original licence link has changed is not relivant.
9988  *
9989  * Fork - LGPL
9990  * <script type="text/javascript">
9991  */
9992
9993 /**
9994  * @class Roo.data.DataProxy
9995  * @extends Roo.data.Observable
9996  * This class is an abstract base class for implementations which provide retrieval of
9997  * unformatted data objects.<br>
9998  * <p>
9999  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10000  * (of the appropriate type which knows how to parse the data object) to provide a block of
10001  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10002  * <p>
10003  * Custom implementations must implement the load method as described in
10004  * {@link Roo.data.HttpProxy#load}.
10005  */
10006 Roo.data.DataProxy = function(){
10007     this.addEvents({
10008         /**
10009          * @event beforeload
10010          * Fires before a network request is made to retrieve a data object.
10011          * @param {Object} This DataProxy object.
10012          * @param {Object} params The params parameter to the load function.
10013          */
10014         beforeload : true,
10015         /**
10016          * @event load
10017          * Fires before the load method's callback is called.
10018          * @param {Object} This DataProxy object.
10019          * @param {Object} o The data object.
10020          * @param {Object} arg The callback argument object passed to the load function.
10021          */
10022         load : true,
10023         /**
10024          * @event loadexception
10025          * Fires if an Exception occurs during data retrieval.
10026          * @param {Object} This DataProxy object.
10027          * @param {Object} o The data object.
10028          * @param {Object} arg The callback argument object passed to the load function.
10029          * @param {Object} e The Exception.
10030          */
10031         loadexception : true
10032     });
10033     Roo.data.DataProxy.superclass.constructor.call(this);
10034 };
10035
10036 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10037
10038     /**
10039      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10040      */
10041 /*
10042  * Based on:
10043  * Ext JS Library 1.1.1
10044  * Copyright(c) 2006-2007, Ext JS, LLC.
10045  *
10046  * Originally Released Under LGPL - original licence link has changed is not relivant.
10047  *
10048  * Fork - LGPL
10049  * <script type="text/javascript">
10050  */
10051 /**
10052  * @class Roo.data.MemoryProxy
10053  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10054  * to the Reader when its load method is called.
10055  * @constructor
10056  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10057  */
10058 Roo.data.MemoryProxy = function(data){
10059     if (data.data) {
10060         data = data.data;
10061     }
10062     Roo.data.MemoryProxy.superclass.constructor.call(this);
10063     this.data = data;
10064 };
10065
10066 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10067     /**
10068      * Load data from the requested source (in this case an in-memory
10069      * data object passed to the constructor), read the data object into
10070      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10071      * process that block using the passed callback.
10072      * @param {Object} params This parameter is not used by the MemoryProxy class.
10073      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10074      * object into a block of Roo.data.Records.
10075      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10076      * The function must be passed <ul>
10077      * <li>The Record block object</li>
10078      * <li>The "arg" argument from the load function</li>
10079      * <li>A boolean success indicator</li>
10080      * </ul>
10081      * @param {Object} scope The scope in which to call the callback
10082      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10083      */
10084     load : function(params, reader, callback, scope, arg){
10085         params = params || {};
10086         var result;
10087         try {
10088             result = reader.readRecords(this.data);
10089         }catch(e){
10090             this.fireEvent("loadexception", this, arg, null, e);
10091             callback.call(scope, null, arg, false);
10092             return;
10093         }
10094         callback.call(scope, result, arg, true);
10095     },
10096     
10097     // private
10098     update : function(params, records){
10099         
10100     }
10101 });/*
10102  * Based on:
10103  * Ext JS Library 1.1.1
10104  * Copyright(c) 2006-2007, Ext JS, LLC.
10105  *
10106  * Originally Released Under LGPL - original licence link has changed is not relivant.
10107  *
10108  * Fork - LGPL
10109  * <script type="text/javascript">
10110  */
10111 /**
10112  * @class Roo.data.HttpProxy
10113  * @extends Roo.data.DataProxy
10114  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10115  * configured to reference a certain URL.<br><br>
10116  * <p>
10117  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10118  * from which the running page was served.<br><br>
10119  * <p>
10120  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10121  * <p>
10122  * Be aware that to enable the browser to parse an XML document, the server must set
10123  * the Content-Type header in the HTTP response to "text/xml".
10124  * @constructor
10125  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10126  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10127  * will be used to make the request.
10128  */
10129 Roo.data.HttpProxy = function(conn){
10130     Roo.data.HttpProxy.superclass.constructor.call(this);
10131     // is conn a conn config or a real conn?
10132     this.conn = conn;
10133     this.useAjax = !conn || !conn.events;
10134   
10135 };
10136
10137 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10138     // thse are take from connection...
10139     
10140     /**
10141      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10142      */
10143     /**
10144      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10145      * extra parameters to each request made by this object. (defaults to undefined)
10146      */
10147     /**
10148      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10149      *  to each request made by this object. (defaults to undefined)
10150      */
10151     /**
10152      * @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)
10153      */
10154     /**
10155      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10156      */
10157      /**
10158      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10159      * @type Boolean
10160      */
10161   
10162
10163     /**
10164      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10165      * @type Boolean
10166      */
10167     /**
10168      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10169      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10170      * a finer-grained basis than the DataProxy events.
10171      */
10172     getConnection : function(){
10173         return this.useAjax ? Roo.Ajax : this.conn;
10174     },
10175
10176     /**
10177      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10178      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10179      * process that block using the passed callback.
10180      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10181      * for the request to the remote server.
10182      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10183      * object into a block of Roo.data.Records.
10184      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10185      * The function must be passed <ul>
10186      * <li>The Record block object</li>
10187      * <li>The "arg" argument from the load function</li>
10188      * <li>A boolean success indicator</li>
10189      * </ul>
10190      * @param {Object} scope The scope in which to call the callback
10191      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10192      */
10193     load : function(params, reader, callback, scope, arg){
10194         if(this.fireEvent("beforeload", this, params) !== false){
10195             var  o = {
10196                 params : params || {},
10197                 request: {
10198                     callback : callback,
10199                     scope : scope,
10200                     arg : arg
10201                 },
10202                 reader: reader,
10203                 callback : this.loadResponse,
10204                 scope: this
10205             };
10206             if(this.useAjax){
10207                 Roo.applyIf(o, this.conn);
10208                 if(this.activeRequest){
10209                     Roo.Ajax.abort(this.activeRequest);
10210                 }
10211                 this.activeRequest = Roo.Ajax.request(o);
10212             }else{
10213                 this.conn.request(o);
10214             }
10215         }else{
10216             callback.call(scope||this, null, arg, false);
10217         }
10218     },
10219
10220     // private
10221     loadResponse : function(o, success, response){
10222         delete this.activeRequest;
10223         if(!success){
10224             this.fireEvent("loadexception", this, o, response);
10225             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10226             return;
10227         }
10228         var result;
10229         try {
10230             result = o.reader.read(response);
10231         }catch(e){
10232             this.fireEvent("loadexception", this, o, response, e);
10233             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10234             return;
10235         }
10236         
10237         this.fireEvent("load", this, o, o.request.arg);
10238         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10239     },
10240
10241     // private
10242     update : function(dataSet){
10243
10244     },
10245
10246     // private
10247     updateResponse : function(dataSet){
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 /**
10262  * @class Roo.data.ScriptTagProxy
10263  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10264  * other than the originating domain of the running page.<br><br>
10265  * <p>
10266  * <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
10267  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10268  * <p>
10269  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10270  * source code that is used as the source inside a &lt;script> tag.<br><br>
10271  * <p>
10272  * In order for the browser to process the returned data, the server must wrap the data object
10273  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10274  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10275  * depending on whether the callback name was passed:
10276  * <p>
10277  * <pre><code>
10278 boolean scriptTag = false;
10279 String cb = request.getParameter("callback");
10280 if (cb != null) {
10281     scriptTag = true;
10282     response.setContentType("text/javascript");
10283 } else {
10284     response.setContentType("application/x-json");
10285 }
10286 Writer out = response.getWriter();
10287 if (scriptTag) {
10288     out.write(cb + "(");
10289 }
10290 out.print(dataBlock.toJsonString());
10291 if (scriptTag) {
10292     out.write(");");
10293 }
10294 </pre></code>
10295  *
10296  * @constructor
10297  * @param {Object} config A configuration object.
10298  */
10299 Roo.data.ScriptTagProxy = function(config){
10300     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10301     Roo.apply(this, config);
10302     this.head = document.getElementsByTagName("head")[0];
10303 };
10304
10305 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10306
10307 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10308     /**
10309      * @cfg {String} url The URL from which to request the data object.
10310      */
10311     /**
10312      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10313      */
10314     timeout : 30000,
10315     /**
10316      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10317      * the server the name of the callback function set up by the load call to process the returned data object.
10318      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10319      * javascript output which calls this named function passing the data object as its only parameter.
10320      */
10321     callbackParam : "callback",
10322     /**
10323      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10324      * name to the request.
10325      */
10326     nocache : true,
10327
10328     /**
10329      * Load data from the configured URL, read the data object into
10330      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10331      * process that block using the passed callback.
10332      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10333      * for the request to the remote server.
10334      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10335      * object into a block of Roo.data.Records.
10336      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10337      * The function must be passed <ul>
10338      * <li>The Record block object</li>
10339      * <li>The "arg" argument from the load function</li>
10340      * <li>A boolean success indicator</li>
10341      * </ul>
10342      * @param {Object} scope The scope in which to call the callback
10343      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10344      */
10345     load : function(params, reader, callback, scope, arg){
10346         if(this.fireEvent("beforeload", this, params) !== false){
10347
10348             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10349
10350             var url = this.url;
10351             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10352             if(this.nocache){
10353                 url += "&_dc=" + (new Date().getTime());
10354             }
10355             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10356             var trans = {
10357                 id : transId,
10358                 cb : "stcCallback"+transId,
10359                 scriptId : "stcScript"+transId,
10360                 params : params,
10361                 arg : arg,
10362                 url : url,
10363                 callback : callback,
10364                 scope : scope,
10365                 reader : reader
10366             };
10367             var conn = this;
10368
10369             window[trans.cb] = function(o){
10370                 conn.handleResponse(o, trans);
10371             };
10372
10373             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10374
10375             if(this.autoAbort !== false){
10376                 this.abort();
10377             }
10378
10379             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10380
10381             var script = document.createElement("script");
10382             script.setAttribute("src", url);
10383             script.setAttribute("type", "text/javascript");
10384             script.setAttribute("id", trans.scriptId);
10385             this.head.appendChild(script);
10386
10387             this.trans = trans;
10388         }else{
10389             callback.call(scope||this, null, arg, false);
10390         }
10391     },
10392
10393     // private
10394     isLoading : function(){
10395         return this.trans ? true : false;
10396     },
10397
10398     /**
10399      * Abort the current server request.
10400      */
10401     abort : function(){
10402         if(this.isLoading()){
10403             this.destroyTrans(this.trans);
10404         }
10405     },
10406
10407     // private
10408     destroyTrans : function(trans, isLoaded){
10409         this.head.removeChild(document.getElementById(trans.scriptId));
10410         clearTimeout(trans.timeoutId);
10411         if(isLoaded){
10412             window[trans.cb] = undefined;
10413             try{
10414                 delete window[trans.cb];
10415             }catch(e){}
10416         }else{
10417             // if hasn't been loaded, wait for load to remove it to prevent script error
10418             window[trans.cb] = function(){
10419                 window[trans.cb] = undefined;
10420                 try{
10421                     delete window[trans.cb];
10422                 }catch(e){}
10423             };
10424         }
10425     },
10426
10427     // private
10428     handleResponse : function(o, trans){
10429         this.trans = false;
10430         this.destroyTrans(trans, true);
10431         var result;
10432         try {
10433             result = trans.reader.readRecords(o);
10434         }catch(e){
10435             this.fireEvent("loadexception", this, o, trans.arg, e);
10436             trans.callback.call(trans.scope||window, null, trans.arg, false);
10437             return;
10438         }
10439         this.fireEvent("load", this, o, trans.arg);
10440         trans.callback.call(trans.scope||window, result, trans.arg, true);
10441     },
10442
10443     // private
10444     handleFailure : function(trans){
10445         this.trans = false;
10446         this.destroyTrans(trans, false);
10447         this.fireEvent("loadexception", this, null, trans.arg);
10448         trans.callback.call(trans.scope||window, null, trans.arg, false);
10449     }
10450 });/*
10451  * Based on:
10452  * Ext JS Library 1.1.1
10453  * Copyright(c) 2006-2007, Ext JS, LLC.
10454  *
10455  * Originally Released Under LGPL - original licence link has changed is not relivant.
10456  *
10457  * Fork - LGPL
10458  * <script type="text/javascript">
10459  */
10460
10461 /**
10462  * @class Roo.data.JsonReader
10463  * @extends Roo.data.DataReader
10464  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10465  * based on mappings in a provided Roo.data.Record constructor.
10466  * 
10467  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10468  * in the reply previously. 
10469  * 
10470  * <p>
10471  * Example code:
10472  * <pre><code>
10473 var RecordDef = Roo.data.Record.create([
10474     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10475     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10476 ]);
10477 var myReader = new Roo.data.JsonReader({
10478     totalProperty: "results",    // The property which contains the total dataset size (optional)
10479     root: "rows",                // The property which contains an Array of row objects
10480     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10481 }, RecordDef);
10482 </code></pre>
10483  * <p>
10484  * This would consume a JSON file like this:
10485  * <pre><code>
10486 { 'results': 2, 'rows': [
10487     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10488     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10489 }
10490 </code></pre>
10491  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10492  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10493  * paged from the remote server.
10494  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10495  * @cfg {String} root name of the property which contains the Array of row objects.
10496  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10497  * @constructor
10498  * Create a new JsonReader
10499  * @param {Object} meta Metadata configuration options
10500  * @param {Object} recordType Either an Array of field definition objects,
10501  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10502  */
10503 Roo.data.JsonReader = function(meta, recordType){
10504     
10505     meta = meta || {};
10506     // set some defaults:
10507     Roo.applyIf(meta, {
10508         totalProperty: 'total',
10509         successProperty : 'success',
10510         root : 'data',
10511         id : 'id'
10512     });
10513     
10514     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10515 };
10516 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10517     
10518     /**
10519      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10520      * Used by Store query builder to append _requestMeta to params.
10521      * 
10522      */
10523     metaFromRemote : false,
10524     /**
10525      * This method is only used by a DataProxy which has retrieved data from a remote server.
10526      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10527      * @return {Object} data A data block which is used by an Roo.data.Store object as
10528      * a cache of Roo.data.Records.
10529      */
10530     read : function(response){
10531         var json = response.responseText;
10532        
10533         var o = /* eval:var:o */ eval("("+json+")");
10534         if(!o) {
10535             throw {message: "JsonReader.read: Json object not found"};
10536         }
10537         
10538         if(o.metaData){
10539             
10540             delete this.ef;
10541             this.metaFromRemote = true;
10542             this.meta = o.metaData;
10543             this.recordType = Roo.data.Record.create(o.metaData.fields);
10544             this.onMetaChange(this.meta, this.recordType, o);
10545         }
10546         return this.readRecords(o);
10547     },
10548
10549     // private function a store will implement
10550     onMetaChange : function(meta, recordType, o){
10551
10552     },
10553
10554     /**
10555          * @ignore
10556          */
10557     simpleAccess: function(obj, subsc) {
10558         return obj[subsc];
10559     },
10560
10561         /**
10562          * @ignore
10563          */
10564     getJsonAccessor: function(){
10565         var re = /[\[\.]/;
10566         return function(expr) {
10567             try {
10568                 return(re.test(expr))
10569                     ? new Function("obj", "return obj." + expr)
10570                     : function(obj){
10571                         return obj[expr];
10572                     };
10573             } catch(e){}
10574             return Roo.emptyFn;
10575         };
10576     }(),
10577
10578     /**
10579      * Create a data block containing Roo.data.Records from an XML document.
10580      * @param {Object} o An object which contains an Array of row objects in the property specified
10581      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10582      * which contains the total size of the dataset.
10583      * @return {Object} data A data block which is used by an Roo.data.Store object as
10584      * a cache of Roo.data.Records.
10585      */
10586     readRecords : function(o){
10587         /**
10588          * After any data loads, the raw JSON data is available for further custom processing.
10589          * @type Object
10590          */
10591         this.o = o;
10592         var s = this.meta, Record = this.recordType,
10593             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10594
10595 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10596         if (!this.ef) {
10597             if(s.totalProperty) {
10598                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10599                 }
10600                 if(s.successProperty) {
10601                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10602                 }
10603                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10604                 if (s.id) {
10605                         var g = this.getJsonAccessor(s.id);
10606                         this.getId = function(rec) {
10607                                 var r = g(rec);  
10608                                 return (r === undefined || r === "") ? null : r;
10609                         };
10610                 } else {
10611                         this.getId = function(){return null;};
10612                 }
10613             this.ef = [];
10614             for(var jj = 0; jj < fl; jj++){
10615                 f = fi[jj];
10616                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10617                 this.ef[jj] = this.getJsonAccessor(map);
10618             }
10619         }
10620
10621         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10622         if(s.totalProperty){
10623             var vt = parseInt(this.getTotal(o), 10);
10624             if(!isNaN(vt)){
10625                 totalRecords = vt;
10626             }
10627         }
10628         if(s.successProperty){
10629             var vs = this.getSuccess(o);
10630             if(vs === false || vs === 'false'){
10631                 success = false;
10632             }
10633         }
10634         var records = [];
10635         for(var i = 0; i < c; i++){
10636                 var n = root[i];
10637             var values = {};
10638             var id = this.getId(n);
10639             for(var j = 0; j < fl; j++){
10640                 f = fi[j];
10641             var v = this.ef[j](n);
10642             if (!f.convert) {
10643                 Roo.log('missing convert for ' + f.name);
10644                 Roo.log(f);
10645                 continue;
10646             }
10647             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10648             }
10649             var record = new Record(values, id);
10650             record.json = n;
10651             records[i] = record;
10652         }
10653         return {
10654             raw : o,
10655             success : success,
10656             records : records,
10657             totalRecords : totalRecords
10658         };
10659     }
10660 });/*
10661  * Based on:
10662  * Ext JS Library 1.1.1
10663  * Copyright(c) 2006-2007, Ext JS, LLC.
10664  *
10665  * Originally Released Under LGPL - original licence link has changed is not relivant.
10666  *
10667  * Fork - LGPL
10668  * <script type="text/javascript">
10669  */
10670
10671 /**
10672  * @class Roo.data.ArrayReader
10673  * @extends Roo.data.DataReader
10674  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10675  * Each element of that Array represents a row of data fields. The
10676  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10677  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10678  * <p>
10679  * Example code:.
10680  * <pre><code>
10681 var RecordDef = Roo.data.Record.create([
10682     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10683     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10684 ]);
10685 var myReader = new Roo.data.ArrayReader({
10686     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10687 }, RecordDef);
10688 </code></pre>
10689  * <p>
10690  * This would consume an Array like this:
10691  * <pre><code>
10692 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10693   </code></pre>
10694  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10695  * @constructor
10696  * Create a new JsonReader
10697  * @param {Object} meta Metadata configuration options.
10698  * @param {Object} recordType Either an Array of field definition objects
10699  * as specified to {@link Roo.data.Record#create},
10700  * or an {@link Roo.data.Record} object
10701  * created using {@link Roo.data.Record#create}.
10702  */
10703 Roo.data.ArrayReader = function(meta, recordType){
10704     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10705 };
10706
10707 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10708     /**
10709      * Create a data block containing Roo.data.Records from an XML document.
10710      * @param {Object} o An Array of row objects which represents the dataset.
10711      * @return {Object} data A data block which is used by an Roo.data.Store object as
10712      * a cache of Roo.data.Records.
10713      */
10714     readRecords : function(o){
10715         var sid = this.meta ? this.meta.id : null;
10716         var recordType = this.recordType, fields = recordType.prototype.fields;
10717         var records = [];
10718         var root = o;
10719             for(var i = 0; i < root.length; i++){
10720                     var n = root[i];
10721                 var values = {};
10722                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10723                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10724                 var f = fields.items[j];
10725                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10726                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10727                 v = f.convert(v);
10728                 values[f.name] = v;
10729             }
10730                 var record = new recordType(values, id);
10731                 record.json = n;
10732                 records[records.length] = record;
10733             }
10734             return {
10735                 records : records,
10736                 totalRecords : records.length
10737             };
10738     }
10739 });/*
10740  * - LGPL
10741  * * 
10742  */
10743
10744 /**
10745  * @class Roo.bootstrap.ComboBox
10746  * @extends Roo.bootstrap.TriggerField
10747  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10748  * @cfg {Boolean} append (true|false) default false
10749  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10750  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10751  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10752  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10753  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10754  * @constructor
10755  * Create a new ComboBox.
10756  * @param {Object} config Configuration options
10757  */
10758 Roo.bootstrap.ComboBox = function(config){
10759     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10760     this.addEvents({
10761         /**
10762          * @event expand
10763          * Fires when the dropdown list is expanded
10764              * @param {Roo.bootstrap.ComboBox} combo This combo box
10765              */
10766         'expand' : true,
10767         /**
10768          * @event collapse
10769          * Fires when the dropdown list is collapsed
10770              * @param {Roo.bootstrap.ComboBox} combo This combo box
10771              */
10772         'collapse' : true,
10773         /**
10774          * @event beforeselect
10775          * Fires before a list item is selected. Return false to cancel the selection.
10776              * @param {Roo.bootstrap.ComboBox} combo This combo box
10777              * @param {Roo.data.Record} record The data record returned from the underlying store
10778              * @param {Number} index The index of the selected item in the dropdown list
10779              */
10780         'beforeselect' : true,
10781         /**
10782          * @event select
10783          * Fires when a list item is selected
10784              * @param {Roo.bootstrap.ComboBox} combo This combo box
10785              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10786              * @param {Number} index The index of the selected item in the dropdown list
10787              */
10788         'select' : true,
10789         /**
10790          * @event beforequery
10791          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10792          * The event object passed has these properties:
10793              * @param {Roo.bootstrap.ComboBox} combo This combo box
10794              * @param {String} query The query
10795              * @param {Boolean} forceAll true to force "all" query
10796              * @param {Boolean} cancel true to cancel the query
10797              * @param {Object} e The query event object
10798              */
10799         'beforequery': true,
10800          /**
10801          * @event add
10802          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10803              * @param {Roo.bootstrap.ComboBox} combo This combo box
10804              */
10805         'add' : true,
10806         /**
10807          * @event edit
10808          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10809              * @param {Roo.bootstrap.ComboBox} combo This combo box
10810              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10811              */
10812         'edit' : true,
10813         /**
10814          * @event remove
10815          * Fires when the remove value from the combobox array
10816              * @param {Roo.bootstrap.ComboBox} combo This combo box
10817              */
10818         'remove' : true,
10819         /**
10820          * @event specialfilter
10821          * Fires when specialfilter
10822             * @param {Roo.bootstrap.ComboBox} combo This combo box
10823             */
10824         'specialfilter' : true
10825         
10826     });
10827     
10828     this.item = [];
10829     this.tickItems = [];
10830     
10831     this.selectedIndex = -1;
10832     if(this.mode == 'local'){
10833         if(config.queryDelay === undefined){
10834             this.queryDelay = 10;
10835         }
10836         if(config.minChars === undefined){
10837             this.minChars = 0;
10838         }
10839     }
10840 };
10841
10842 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10843      
10844     /**
10845      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10846      * rendering into an Roo.Editor, defaults to false)
10847      */
10848     /**
10849      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10850      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10851      */
10852     /**
10853      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10854      */
10855     /**
10856      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10857      * the dropdown list (defaults to undefined, with no header element)
10858      */
10859
10860      /**
10861      * @cfg {String/Roo.Template} tpl The template to use to render the output
10862      */
10863      
10864      /**
10865      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10866      */
10867     listWidth: undefined,
10868     /**
10869      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10870      * mode = 'remote' or 'text' if mode = 'local')
10871      */
10872     displayField: undefined,
10873     
10874     /**
10875      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10876      * mode = 'remote' or 'value' if mode = 'local'). 
10877      * Note: use of a valueField requires the user make a selection
10878      * in order for a value to be mapped.
10879      */
10880     valueField: undefined,
10881     
10882     
10883     /**
10884      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10885      * field's data value (defaults to the underlying DOM element's name)
10886      */
10887     hiddenName: undefined,
10888     /**
10889      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10890      */
10891     listClass: '',
10892     /**
10893      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10894      */
10895     selectedClass: 'active',
10896     
10897     /**
10898      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10899      */
10900     shadow:'sides',
10901     /**
10902      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10903      * anchor positions (defaults to 'tl-bl')
10904      */
10905     listAlign: 'tl-bl?',
10906     /**
10907      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10908      */
10909     maxHeight: 300,
10910     /**
10911      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10912      * query specified by the allQuery config option (defaults to 'query')
10913      */
10914     triggerAction: 'query',
10915     /**
10916      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10917      * (defaults to 4, does not apply if editable = false)
10918      */
10919     minChars : 4,
10920     /**
10921      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10922      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10923      */
10924     typeAhead: false,
10925     /**
10926      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10927      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10928      */
10929     queryDelay: 500,
10930     /**
10931      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10932      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10933      */
10934     pageSize: 0,
10935     /**
10936      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10937      * when editable = true (defaults to false)
10938      */
10939     selectOnFocus:false,
10940     /**
10941      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10942      */
10943     queryParam: 'query',
10944     /**
10945      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10946      * when mode = 'remote' (defaults to 'Loading...')
10947      */
10948     loadingText: 'Loading...',
10949     /**
10950      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10951      */
10952     resizable: false,
10953     /**
10954      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10955      */
10956     handleHeight : 8,
10957     /**
10958      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10959      * traditional select (defaults to true)
10960      */
10961     editable: true,
10962     /**
10963      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10964      */
10965     allQuery: '',
10966     /**
10967      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10968      */
10969     mode: 'remote',
10970     /**
10971      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10972      * listWidth has a higher value)
10973      */
10974     minListWidth : 70,
10975     /**
10976      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10977      * allow the user to set arbitrary text into the field (defaults to false)
10978      */
10979     forceSelection:false,
10980     /**
10981      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10982      * if typeAhead = true (defaults to 250)
10983      */
10984     typeAheadDelay : 250,
10985     /**
10986      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10987      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10988      */
10989     valueNotFoundText : undefined,
10990     /**
10991      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10992      */
10993     blockFocus : false,
10994     
10995     /**
10996      * @cfg {Boolean} disableClear Disable showing of clear button.
10997      */
10998     disableClear : false,
10999     /**
11000      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11001      */
11002     alwaysQuery : false,
11003     
11004     /**
11005      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11006      */
11007     multiple : false,
11008     
11009     /**
11010      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11011      */
11012     invalidClass : "has-warning",
11013     
11014     /**
11015      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11016      */
11017     validClass : "has-success",
11018     
11019     /**
11020      * @cfg {Boolean} specialFilter (true|false) special filter default false
11021      */
11022     specialFilter : false,
11023     
11024     //private
11025     addicon : false,
11026     editicon: false,
11027     
11028     page: 0,
11029     hasQuery: false,
11030     append: false,
11031     loadNext: false,
11032     autoFocus : true,
11033     tickable : false,
11034     btnPosition : 'right',
11035     triggerList : true,
11036     showToggleBtn : true,
11037     // element that contains real text value.. (when hidden is used..)
11038     
11039     getAutoCreate : function()
11040     {
11041         var cfg = false;
11042         
11043         /*
11044          *  Normal ComboBox
11045          */
11046         if(!this.tickable){
11047             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11048             return cfg;
11049         }
11050         
11051         /*
11052          *  ComboBox with tickable selections
11053          */
11054              
11055         var align = this.labelAlign || this.parentLabelAlign();
11056         
11057         cfg = {
11058             cls : 'form-group roo-combobox-tickable' //input-group
11059         };
11060         
11061         var buttons = {
11062             tag : 'div',
11063             cls : 'tickable-buttons',
11064             cn : [
11065                 {
11066                     tag : 'button',
11067                     type : 'button',
11068                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11069                     html : 'Edit'
11070                 },
11071                 {
11072                     tag : 'button',
11073                     type : 'button',
11074                     name : 'ok',
11075                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11076                     html : 'Done'
11077                 },
11078                 {
11079                     tag : 'button',
11080                     type : 'button',
11081                     name : 'cancel',
11082                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11083                     html : 'Cancel'
11084                 }
11085             ]
11086         };
11087         
11088         if(this.editable){
11089             buttons.cn.unshift({
11090                 tag: 'input',
11091                 cls: 'select2-search-field-input'
11092             });
11093         }
11094         
11095         var _this = this;
11096         
11097         Roo.each(buttons.cn, function(c){
11098             if (_this.size) {
11099                 c.cls += ' btn-' + _this.size;
11100             }
11101
11102             if (_this.disabled) {
11103                 c.disabled = true;
11104             }
11105         });
11106         
11107         var box = {
11108             tag: 'div',
11109             cn: [
11110                 {
11111                     tag: 'input',
11112                     type : 'hidden',
11113                     cls: 'form-hidden-field'
11114                 },
11115                 {
11116                     tag: 'ul',
11117                     cls: 'select2-choices',
11118                     cn:[
11119                         {
11120                             tag: 'li',
11121                             cls: 'select2-search-field',
11122                             cn: [
11123
11124                                 buttons
11125                             ]
11126                         }
11127                     ]
11128                 }
11129             ]
11130         }
11131         
11132         var combobox = {
11133             cls: 'select2-container input-group select2-container-multi',
11134             cn: [
11135                 box
11136 //                {
11137 //                    tag: 'ul',
11138 //                    cls: 'typeahead typeahead-long dropdown-menu',
11139 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11140 //                }
11141             ]
11142         };
11143         
11144         if(this.hasFeedback && !this.allowBlank){
11145             
11146             var feedback = {
11147                 tag: 'span',
11148                 cls: 'glyphicon form-control-feedback'
11149             };
11150
11151             combobox.cn.push(feedback);
11152         }
11153         
11154         if (align ==='left' && this.fieldLabel.length) {
11155             
11156                 Roo.log("left and has label");
11157                 cfg.cn = [
11158                     
11159                     {
11160                         tag: 'label',
11161                         'for' :  id,
11162                         cls : 'control-label col-sm-' + this.labelWidth,
11163                         html : this.fieldLabel
11164                         
11165                     },
11166                     {
11167                         cls : "col-sm-" + (12 - this.labelWidth), 
11168                         cn: [
11169                             combobox
11170                         ]
11171                     }
11172                     
11173                 ];
11174         } else if ( this.fieldLabel.length) {
11175                 Roo.log(" label");
11176                  cfg.cn = [
11177                    
11178                     {
11179                         tag: 'label',
11180                         //cls : 'input-group-addon',
11181                         html : this.fieldLabel
11182                         
11183                     },
11184                     
11185                     combobox
11186                     
11187                 ];
11188
11189         } else {
11190             
11191                 Roo.log(" no label && no align");
11192                 cfg = combobox
11193                      
11194                 
11195         }
11196          
11197         var settings=this;
11198         ['xs','sm','md','lg'].map(function(size){
11199             if (settings[size]) {
11200                 cfg.cls += ' col-' + size + '-' + settings[size];
11201             }
11202         });
11203         
11204         return cfg;
11205         
11206     },
11207     
11208     // private
11209     initEvents: function()
11210     {
11211         
11212         if (!this.store) {
11213             throw "can not find store for combo";
11214         }
11215         this.store = Roo.factory(this.store, Roo.data);
11216         
11217         if(this.tickable){
11218             this.initTickableEvents();
11219             return;
11220         }
11221         
11222         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11223         
11224         if(this.hiddenName){
11225             
11226             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11227             
11228             this.hiddenField.dom.value =
11229                 this.hiddenValue !== undefined ? this.hiddenValue :
11230                 this.value !== undefined ? this.value : '';
11231
11232             // prevent input submission
11233             this.el.dom.removeAttribute('name');
11234             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11235              
11236              
11237         }
11238         //if(Roo.isGecko){
11239         //    this.el.dom.setAttribute('autocomplete', 'off');
11240         //}
11241         
11242         var cls = 'x-combo-list';
11243         
11244         //this.list = new Roo.Layer({
11245         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11246         //});
11247         
11248         var _this = this;
11249         
11250         (function(){
11251             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11252             _this.list.setWidth(lw);
11253         }).defer(100);
11254         
11255         this.list.on('mouseover', this.onViewOver, this);
11256         this.list.on('mousemove', this.onViewMove, this);
11257         
11258         this.list.on('scroll', this.onViewScroll, this);
11259         
11260         /*
11261         this.list.swallowEvent('mousewheel');
11262         this.assetHeight = 0;
11263
11264         if(this.title){
11265             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11266             this.assetHeight += this.header.getHeight();
11267         }
11268
11269         this.innerList = this.list.createChild({cls:cls+'-inner'});
11270         this.innerList.on('mouseover', this.onViewOver, this);
11271         this.innerList.on('mousemove', this.onViewMove, this);
11272         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11273         
11274         if(this.allowBlank && !this.pageSize && !this.disableClear){
11275             this.footer = this.list.createChild({cls:cls+'-ft'});
11276             this.pageTb = new Roo.Toolbar(this.footer);
11277            
11278         }
11279         if(this.pageSize){
11280             this.footer = this.list.createChild({cls:cls+'-ft'});
11281             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11282                     {pageSize: this.pageSize});
11283             
11284         }
11285         
11286         if (this.pageTb && this.allowBlank && !this.disableClear) {
11287             var _this = this;
11288             this.pageTb.add(new Roo.Toolbar.Fill(), {
11289                 cls: 'x-btn-icon x-btn-clear',
11290                 text: '&#160;',
11291                 handler: function()
11292                 {
11293                     _this.collapse();
11294                     _this.clearValue();
11295                     _this.onSelect(false, -1);
11296                 }
11297             });
11298         }
11299         if (this.footer) {
11300             this.assetHeight += this.footer.getHeight();
11301         }
11302         */
11303             
11304         if(!this.tpl){
11305             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11306         }
11307
11308         this.view = new Roo.View(this.list, this.tpl, {
11309             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11310         });
11311         //this.view.wrapEl.setDisplayed(false);
11312         this.view.on('click', this.onViewClick, this);
11313         
11314         
11315         
11316         this.store.on('beforeload', this.onBeforeLoad, this);
11317         this.store.on('load', this.onLoad, this);
11318         this.store.on('loadexception', this.onLoadException, this);
11319         /*
11320         if(this.resizable){
11321             this.resizer = new Roo.Resizable(this.list,  {
11322                pinned:true, handles:'se'
11323             });
11324             this.resizer.on('resize', function(r, w, h){
11325                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11326                 this.listWidth = w;
11327                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11328                 this.restrictHeight();
11329             }, this);
11330             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11331         }
11332         */
11333         if(!this.editable){
11334             this.editable = true;
11335             this.setEditable(false);
11336         }
11337         
11338         /*
11339         
11340         if (typeof(this.events.add.listeners) != 'undefined') {
11341             
11342             this.addicon = this.wrap.createChild(
11343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11344        
11345             this.addicon.on('click', function(e) {
11346                 this.fireEvent('add', this);
11347             }, this);
11348         }
11349         if (typeof(this.events.edit.listeners) != 'undefined') {
11350             
11351             this.editicon = this.wrap.createChild(
11352                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11353             if (this.addicon) {
11354                 this.editicon.setStyle('margin-left', '40px');
11355             }
11356             this.editicon.on('click', function(e) {
11357                 
11358                 // we fire even  if inothing is selected..
11359                 this.fireEvent('edit', this, this.lastData );
11360                 
11361             }, this);
11362         }
11363         */
11364         
11365         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11366             "up" : function(e){
11367                 this.inKeyMode = true;
11368                 this.selectPrev();
11369             },
11370
11371             "down" : function(e){
11372                 if(!this.isExpanded()){
11373                     this.onTriggerClick();
11374                 }else{
11375                     this.inKeyMode = true;
11376                     this.selectNext();
11377                 }
11378             },
11379
11380             "enter" : function(e){
11381 //                this.onViewClick();
11382                 //return true;
11383                 this.collapse();
11384                 
11385                 if(this.fireEvent("specialkey", this, e)){
11386                     this.onViewClick(false);
11387                 }
11388                 
11389                 return true;
11390             },
11391
11392             "esc" : function(e){
11393                 this.collapse();
11394             },
11395
11396             "tab" : function(e){
11397                 this.collapse();
11398                 
11399                 if(this.fireEvent("specialkey", this, e)){
11400                     this.onViewClick(false);
11401                 }
11402                 
11403                 return true;
11404             },
11405
11406             scope : this,
11407
11408             doRelay : function(foo, bar, hname){
11409                 if(hname == 'down' || this.scope.isExpanded()){
11410                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11411                 }
11412                 return true;
11413             },
11414
11415             forceKeyDown: true
11416         });
11417         
11418         
11419         this.queryDelay = Math.max(this.queryDelay || 10,
11420                 this.mode == 'local' ? 10 : 250);
11421         
11422         
11423         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11424         
11425         if(this.typeAhead){
11426             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11427         }
11428         if(this.editable !== false){
11429             this.inputEl().on("keyup", this.onKeyUp, this);
11430         }
11431         if(this.forceSelection){
11432             this.inputEl().on('blur', this.doForce, this);
11433         }
11434         
11435         if(this.multiple){
11436             this.choices = this.el.select('ul.select2-choices', true).first();
11437             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11438         }
11439     },
11440     
11441     initTickableEvents: function()
11442     {   
11443         this.createList();
11444         
11445         if(this.hiddenName){
11446             
11447             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11448             
11449             this.hiddenField.dom.value =
11450                 this.hiddenValue !== undefined ? this.hiddenValue :
11451                 this.value !== undefined ? this.value : '';
11452
11453             // prevent input submission
11454             this.el.dom.removeAttribute('name');
11455             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11456              
11457              
11458         }
11459         
11460 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11461         
11462         this.choices = this.el.select('ul.select2-choices', true).first();
11463         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11464         if(this.triggerList){
11465             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11466         }
11467          
11468         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11469         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11470         
11471         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11472         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11473         
11474         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11475         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11476         
11477         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11478         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11479         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11480         
11481         this.okBtn.hide();
11482         this.cancelBtn.hide();
11483         
11484         var _this = this;
11485         
11486         (function(){
11487             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11488             _this.list.setWidth(lw);
11489         }).defer(100);
11490         
11491         this.list.on('mouseover', this.onViewOver, this);
11492         this.list.on('mousemove', this.onViewMove, this);
11493         
11494         this.list.on('scroll', this.onViewScroll, this);
11495         
11496         if(!this.tpl){
11497             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>';
11498         }
11499
11500         this.view = new Roo.View(this.list, this.tpl, {
11501             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11502         });
11503         
11504         //this.view.wrapEl.setDisplayed(false);
11505         this.view.on('click', this.onViewClick, this);
11506         
11507         
11508         
11509         this.store.on('beforeload', this.onBeforeLoad, this);
11510         this.store.on('load', this.onLoad, this);
11511         this.store.on('loadexception', this.onLoadException, this);
11512         
11513         if(this.editable){
11514             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11515                 "up" : function(e){
11516                     this.inKeyMode = true;
11517                     this.selectPrev();
11518                 },
11519
11520                 "down" : function(e){
11521                     this.inKeyMode = true;
11522                     this.selectNext();
11523                 },
11524
11525                 "enter" : function(e){
11526                     if(this.fireEvent("specialkey", this, e)){
11527                         this.onViewClick(false);
11528                     }
11529                     
11530                     return true;
11531                 },
11532
11533                 "esc" : function(e){
11534                     this.onTickableFooterButtonClick(e, false, false);
11535                 },
11536
11537                 "tab" : function(e){
11538                     this.fireEvent("specialkey", this, e);
11539                     
11540                     this.onTickableFooterButtonClick(e, false, false);
11541                     
11542                     return true;
11543                 },
11544
11545                 scope : this,
11546
11547                 doRelay : function(e, fn, key){
11548                     if(this.scope.isExpanded()){
11549                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11550                     }
11551                     return true;
11552                 },
11553
11554                 forceKeyDown: true
11555             });
11556         }
11557         
11558         this.queryDelay = Math.max(this.queryDelay || 10,
11559                 this.mode == 'local' ? 10 : 250);
11560         
11561         
11562         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11563         
11564         if(this.typeAhead){
11565             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11566         }
11567         
11568         if(this.editable !== false){
11569             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11570         }
11571         
11572     },
11573
11574     onDestroy : function(){
11575         if(this.view){
11576             this.view.setStore(null);
11577             this.view.el.removeAllListeners();
11578             this.view.el.remove();
11579             this.view.purgeListeners();
11580         }
11581         if(this.list){
11582             this.list.dom.innerHTML  = '';
11583         }
11584         
11585         if(this.store){
11586             this.store.un('beforeload', this.onBeforeLoad, this);
11587             this.store.un('load', this.onLoad, this);
11588             this.store.un('loadexception', this.onLoadException, this);
11589         }
11590         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11591     },
11592
11593     // private
11594     fireKey : function(e){
11595         if(e.isNavKeyPress() && !this.list.isVisible()){
11596             this.fireEvent("specialkey", this, e);
11597         }
11598     },
11599
11600     // private
11601     onResize: function(w, h){
11602 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11603 //        
11604 //        if(typeof w != 'number'){
11605 //            // we do not handle it!?!?
11606 //            return;
11607 //        }
11608 //        var tw = this.trigger.getWidth();
11609 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11610 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11611 //        var x = w - tw;
11612 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11613 //            
11614 //        //this.trigger.setStyle('left', x+'px');
11615 //        
11616 //        if(this.list && this.listWidth === undefined){
11617 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11618 //            this.list.setWidth(lw);
11619 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11620 //        }
11621         
11622     
11623         
11624     },
11625
11626     /**
11627      * Allow or prevent the user from directly editing the field text.  If false is passed,
11628      * the user will only be able to select from the items defined in the dropdown list.  This method
11629      * is the runtime equivalent of setting the 'editable' config option at config time.
11630      * @param {Boolean} value True to allow the user to directly edit the field text
11631      */
11632     setEditable : function(value){
11633         if(value == this.editable){
11634             return;
11635         }
11636         this.editable = value;
11637         if(!value){
11638             this.inputEl().dom.setAttribute('readOnly', true);
11639             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11640             this.inputEl().addClass('x-combo-noedit');
11641         }else{
11642             this.inputEl().dom.setAttribute('readOnly', false);
11643             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11644             this.inputEl().removeClass('x-combo-noedit');
11645         }
11646     },
11647
11648     // private
11649     
11650     onBeforeLoad : function(combo,opts){
11651         if(!this.hasFocus){
11652             return;
11653         }
11654          if (!opts.add) {
11655             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11656          }
11657         this.restrictHeight();
11658         this.selectedIndex = -1;
11659     },
11660
11661     // private
11662     onLoad : function(){
11663         
11664         this.hasQuery = false;
11665         
11666         if(!this.hasFocus){
11667             return;
11668         }
11669         
11670         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11671             this.loading.hide();
11672         }
11673              
11674         if(this.store.getCount() > 0){
11675             this.expand();
11676             this.restrictHeight();
11677             if(this.lastQuery == this.allQuery){
11678                 if(this.editable && !this.tickable){
11679                     this.inputEl().dom.select();
11680                 }
11681                 
11682                 if(
11683                     !this.selectByValue(this.value, true) &&
11684                     this.autoFocus && 
11685                     (
11686                         !this.store.lastOptions ||
11687                         typeof(this.store.lastOptions.add) == 'undefined' || 
11688                         this.store.lastOptions.add != true
11689                     )
11690                 ){
11691                     this.select(0, true);
11692                 }
11693             }else{
11694                 if(this.autoFocus){
11695                     this.selectNext();
11696                 }
11697                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11698                     this.taTask.delay(this.typeAheadDelay);
11699                 }
11700             }
11701         }else{
11702             this.onEmptyResults();
11703         }
11704         
11705         //this.el.focus();
11706     },
11707     // private
11708     onLoadException : function()
11709     {
11710         this.hasQuery = false;
11711         
11712         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11713             this.loading.hide();
11714         }
11715         
11716         if(this.tickable && this.editable){
11717             return;
11718         }
11719         
11720         this.collapse();
11721         
11722         Roo.log(this.store.reader.jsonData);
11723         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11724             // fixme
11725             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11726         }
11727         
11728         
11729     },
11730     // private
11731     onTypeAhead : function(){
11732         if(this.store.getCount() > 0){
11733             var r = this.store.getAt(0);
11734             var newValue = r.data[this.displayField];
11735             var len = newValue.length;
11736             var selStart = this.getRawValue().length;
11737             
11738             if(selStart != len){
11739                 this.setRawValue(newValue);
11740                 this.selectText(selStart, newValue.length);
11741             }
11742         }
11743     },
11744
11745     // private
11746     onSelect : function(record, index){
11747         
11748         if(this.fireEvent('beforeselect', this, record, index) !== false){
11749         
11750             this.setFromData(index > -1 ? record.data : false);
11751             
11752             this.collapse();
11753             this.fireEvent('select', this, record, index);
11754         }
11755     },
11756
11757     /**
11758      * Returns the currently selected field value or empty string if no value is set.
11759      * @return {String} value The selected value
11760      */
11761     getValue : function(){
11762         
11763         if(this.multiple){
11764             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11765         }
11766         
11767         if(this.valueField){
11768             return typeof this.value != 'undefined' ? this.value : '';
11769         }else{
11770             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11771         }
11772     },
11773
11774     /**
11775      * Clears any text/value currently set in the field
11776      */
11777     clearValue : function(){
11778         if(this.hiddenField){
11779             this.hiddenField.dom.value = '';
11780         }
11781         this.value = '';
11782         this.setRawValue('');
11783         this.lastSelectionText = '';
11784         this.lastData = false;
11785         
11786     },
11787
11788     /**
11789      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11790      * will be displayed in the field.  If the value does not match the data value of an existing item,
11791      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11792      * Otherwise the field will be blank (although the value will still be set).
11793      * @param {String} value The value to match
11794      */
11795     setValue : function(v){
11796         if(this.multiple){
11797             this.syncValue();
11798             return;
11799         }
11800         
11801         var text = v;
11802         if(this.valueField){
11803             var r = this.findRecord(this.valueField, v);
11804             if(r){
11805                 text = r.data[this.displayField];
11806             }else if(this.valueNotFoundText !== undefined){
11807                 text = this.valueNotFoundText;
11808             }
11809         }
11810         this.lastSelectionText = text;
11811         if(this.hiddenField){
11812             this.hiddenField.dom.value = v;
11813         }
11814         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11815         this.value = v;
11816     },
11817     /**
11818      * @property {Object} the last set data for the element
11819      */
11820     
11821     lastData : false,
11822     /**
11823      * Sets the value of the field based on a object which is related to the record format for the store.
11824      * @param {Object} value the value to set as. or false on reset?
11825      */
11826     setFromData : function(o){
11827         
11828         if(this.multiple){
11829             this.addItem(o);
11830             return;
11831         }
11832             
11833         var dv = ''; // display value
11834         var vv = ''; // value value..
11835         this.lastData = o;
11836         if (this.displayField) {
11837             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11838         } else {
11839             // this is an error condition!!!
11840             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11841         }
11842         
11843         if(this.valueField){
11844             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11845         }
11846         
11847         if(this.hiddenField){
11848             this.hiddenField.dom.value = vv;
11849             
11850             this.lastSelectionText = dv;
11851             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11852             this.value = vv;
11853             return;
11854         }
11855         // no hidden field.. - we store the value in 'value', but still display
11856         // display field!!!!
11857         this.lastSelectionText = dv;
11858         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11859         this.value = vv;
11860         
11861         
11862     },
11863     // private
11864     reset : function(){
11865         // overridden so that last data is reset..
11866         
11867         if(this.multiple){
11868             this.clearItem();
11869             return;
11870         }
11871         
11872         this.setValue(this.originalValue);
11873         this.clearInvalid();
11874         this.lastData = false;
11875         if (this.view) {
11876             this.view.clearSelections();
11877         }
11878     },
11879     // private
11880     findRecord : function(prop, value){
11881         var record;
11882         if(this.store.getCount() > 0){
11883             this.store.each(function(r){
11884                 if(r.data[prop] == value){
11885                     record = r;
11886                     return false;
11887                 }
11888                 return true;
11889             });
11890         }
11891         return record;
11892     },
11893     
11894     getName: function()
11895     {
11896         // returns hidden if it's set..
11897         if (!this.rendered) {return ''};
11898         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11899         
11900     },
11901     // private
11902     onViewMove : function(e, t){
11903         this.inKeyMode = false;
11904     },
11905
11906     // private
11907     onViewOver : function(e, t){
11908         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11909             return;
11910         }
11911         var item = this.view.findItemFromChild(t);
11912         
11913         if(item){
11914             var index = this.view.indexOf(item);
11915             this.select(index, false);
11916         }
11917     },
11918
11919     // private
11920     onViewClick : function(view, doFocus, el, e)
11921     {
11922         var index = this.view.getSelectedIndexes()[0];
11923         
11924         var r = this.store.getAt(index);
11925         
11926         if(this.tickable){
11927             
11928             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11929                 return;
11930             }
11931             
11932             var rm = false;
11933             var _this = this;
11934             
11935             Roo.each(this.tickItems, function(v,k){
11936                 
11937                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11938                     _this.tickItems.splice(k, 1);
11939                     
11940                     if(typeof(e) == 'undefined' && view == false){
11941                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11942                     }
11943                     
11944                     rm = true;
11945                     return;
11946                 }
11947             });
11948             
11949             if(rm){
11950                 return;
11951             }
11952             
11953             this.tickItems.push(r.data);
11954             
11955             if(typeof(e) == 'undefined' && view == false){
11956                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11957             }
11958                     
11959             return;
11960         }
11961         
11962         if(r){
11963             this.onSelect(r, index);
11964         }
11965         if(doFocus !== false && !this.blockFocus){
11966             this.inputEl().focus();
11967         }
11968     },
11969
11970     // private
11971     restrictHeight : function(){
11972         //this.innerList.dom.style.height = '';
11973         //var inner = this.innerList.dom;
11974         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11975         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11976         //this.list.beginUpdate();
11977         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11978         this.list.alignTo(this.inputEl(), this.listAlign);
11979         this.list.alignTo(this.inputEl(), this.listAlign);
11980         //this.list.endUpdate();
11981     },
11982
11983     // private
11984     onEmptyResults : function(){
11985         
11986         if(this.tickable && this.editable){
11987             this.restrictHeight();
11988             return;
11989         }
11990         
11991         this.collapse();
11992     },
11993
11994     /**
11995      * Returns true if the dropdown list is expanded, else false.
11996      */
11997     isExpanded : function(){
11998         return this.list.isVisible();
11999     },
12000
12001     /**
12002      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12003      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12004      * @param {String} value The data value of the item to select
12005      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12006      * selected item if it is not currently in view (defaults to true)
12007      * @return {Boolean} True if the value matched an item in the list, else false
12008      */
12009     selectByValue : function(v, scrollIntoView){
12010         if(v !== undefined && v !== null){
12011             var r = this.findRecord(this.valueField || this.displayField, v);
12012             if(r){
12013                 this.select(this.store.indexOf(r), scrollIntoView);
12014                 return true;
12015             }
12016         }
12017         return false;
12018     },
12019
12020     /**
12021      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12022      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12023      * @param {Number} index The zero-based index of the list item to select
12024      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12025      * selected item if it is not currently in view (defaults to true)
12026      */
12027     select : function(index, scrollIntoView){
12028         this.selectedIndex = index;
12029         this.view.select(index);
12030         if(scrollIntoView !== false){
12031             var el = this.view.getNode(index);
12032             /*
12033              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12034              */
12035             if(el){
12036                 this.list.scrollChildIntoView(el, false);
12037             }
12038         }
12039     },
12040
12041     // private
12042     selectNext : function(){
12043         var ct = this.store.getCount();
12044         if(ct > 0){
12045             if(this.selectedIndex == -1){
12046                 this.select(0);
12047             }else if(this.selectedIndex < ct-1){
12048                 this.select(this.selectedIndex+1);
12049             }
12050         }
12051     },
12052
12053     // private
12054     selectPrev : function(){
12055         var ct = this.store.getCount();
12056         if(ct > 0){
12057             if(this.selectedIndex == -1){
12058                 this.select(0);
12059             }else if(this.selectedIndex != 0){
12060                 this.select(this.selectedIndex-1);
12061             }
12062         }
12063     },
12064
12065     // private
12066     onKeyUp : function(e){
12067         if(this.editable !== false && !e.isSpecialKey()){
12068             this.lastKey = e.getKey();
12069             this.dqTask.delay(this.queryDelay);
12070         }
12071     },
12072
12073     // private
12074     validateBlur : function(){
12075         return !this.list || !this.list.isVisible();   
12076     },
12077
12078     // private
12079     initQuery : function(){
12080         
12081         var v = this.getRawValue();
12082         
12083         if(this.tickable && this.editable){
12084             v = this.tickableInputEl().getValue();
12085         }
12086         
12087         this.doQuery(v);
12088     },
12089
12090     // private
12091     doForce : function(){
12092         if(this.inputEl().dom.value.length > 0){
12093             this.inputEl().dom.value =
12094                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12095              
12096         }
12097     },
12098
12099     /**
12100      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12101      * query allowing the query action to be canceled if needed.
12102      * @param {String} query The SQL query to execute
12103      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12104      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12105      * saved in the current store (defaults to false)
12106      */
12107     doQuery : function(q, forceAll){
12108         
12109         if(q === undefined || q === null){
12110             q = '';
12111         }
12112         var qe = {
12113             query: q,
12114             forceAll: forceAll,
12115             combo: this,
12116             cancel:false
12117         };
12118         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12119             return false;
12120         }
12121         q = qe.query;
12122         
12123         forceAll = qe.forceAll;
12124         if(forceAll === true || (q.length >= this.minChars)){
12125             
12126             this.hasQuery = true;
12127             
12128             if(this.lastQuery != q || this.alwaysQuery){
12129                 this.lastQuery = q;
12130                 if(this.mode == 'local'){
12131                     this.selectedIndex = -1;
12132                     if(forceAll){
12133                         this.store.clearFilter();
12134                     }else{
12135                         
12136                         if(this.specialFilter){
12137                             this.fireEvent('specialfilter', this);
12138                             this.onLoad();
12139                             return;
12140                         }
12141                         
12142                         this.store.filter(this.displayField, q);
12143                     }
12144                     
12145                     this.store.fireEvent("datachanged", this.store);
12146                     
12147                     this.onLoad();
12148                     
12149                     
12150                 }else{
12151                     
12152                     this.store.baseParams[this.queryParam] = q;
12153                     
12154                     var options = {params : this.getParams(q)};
12155                     
12156                     if(this.loadNext){
12157                         options.add = true;
12158                         options.params.start = this.page * this.pageSize;
12159                     }
12160                     
12161                     this.store.load(options);
12162                     
12163                     /*
12164                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12165                      *  we should expand the list on onLoad
12166                      *  so command out it
12167                      */
12168 //                    this.expand();
12169                 }
12170             }else{
12171                 this.selectedIndex = -1;
12172                 this.onLoad();   
12173             }
12174         }
12175         
12176         this.loadNext = false;
12177     },
12178     
12179     // private
12180     getParams : function(q){
12181         var p = {};
12182         //p[this.queryParam] = q;
12183         
12184         if(this.pageSize){
12185             p.start = 0;
12186             p.limit = this.pageSize;
12187         }
12188         return p;
12189     },
12190
12191     /**
12192      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12193      */
12194     collapse : function(){
12195         if(!this.isExpanded()){
12196             return;
12197         }
12198         
12199         this.list.hide();
12200         
12201         if(this.tickable){
12202             this.hasFocus = false;
12203             this.okBtn.hide();
12204             this.cancelBtn.hide();
12205             this.trigger.show();
12206             
12207             if(this.editable){
12208                 this.tickableInputEl().dom.value = '';
12209                 this.tickableInputEl().blur();
12210             }
12211             
12212         }
12213         
12214         Roo.get(document).un('mousedown', this.collapseIf, this);
12215         Roo.get(document).un('mousewheel', this.collapseIf, this);
12216         if (!this.editable) {
12217             Roo.get(document).un('keydown', this.listKeyPress, this);
12218         }
12219         this.fireEvent('collapse', this);
12220     },
12221
12222     // private
12223     collapseIf : function(e){
12224         var in_combo  = e.within(this.el);
12225         var in_list =  e.within(this.list);
12226         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12227         
12228         if (in_combo || in_list || is_list) {
12229             //e.stopPropagation();
12230             return;
12231         }
12232         
12233         if(this.tickable){
12234             this.onTickableFooterButtonClick(e, false, false);
12235         }
12236
12237         this.collapse();
12238         
12239     },
12240
12241     /**
12242      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12243      */
12244     expand : function(){
12245        
12246         if(this.isExpanded() || !this.hasFocus){
12247             return;
12248         }
12249         
12250         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12251         this.list.setWidth(lw);
12252         
12253         
12254          Roo.log('expand');
12255         
12256         this.list.show();
12257         
12258         this.restrictHeight();
12259         
12260         if(this.tickable){
12261             
12262             this.tickItems = Roo.apply([], this.item);
12263             
12264             this.okBtn.show();
12265             this.cancelBtn.show();
12266             this.trigger.hide();
12267             
12268             if(this.editable){
12269                 this.tickableInputEl().focus();
12270             }
12271             
12272         }
12273         
12274         Roo.get(document).on('mousedown', this.collapseIf, this);
12275         Roo.get(document).on('mousewheel', this.collapseIf, this);
12276         if (!this.editable) {
12277             Roo.get(document).on('keydown', this.listKeyPress, this);
12278         }
12279         
12280         this.fireEvent('expand', this);
12281     },
12282
12283     // private
12284     // Implements the default empty TriggerField.onTriggerClick function
12285     onTriggerClick : function(e)
12286     {
12287         Roo.log('trigger click');
12288         
12289         if(this.disabled || !this.triggerList){
12290             return;
12291         }
12292         
12293         this.page = 0;
12294         this.loadNext = false;
12295         
12296         if(this.isExpanded()){
12297             this.collapse();
12298             if (!this.blockFocus) {
12299                 this.inputEl().focus();
12300             }
12301             
12302         }else {
12303             this.hasFocus = true;
12304             if(this.triggerAction == 'all') {
12305                 this.doQuery(this.allQuery, true);
12306             } else {
12307                 this.doQuery(this.getRawValue());
12308             }
12309             if (!this.blockFocus) {
12310                 this.inputEl().focus();
12311             }
12312         }
12313     },
12314     
12315     onTickableTriggerClick : function(e)
12316     {
12317         if(this.disabled){
12318             return;
12319         }
12320         
12321         this.page = 0;
12322         this.loadNext = false;
12323         this.hasFocus = true;
12324         
12325         if(this.triggerAction == 'all') {
12326             this.doQuery(this.allQuery, true);
12327         } else {
12328             this.doQuery(this.getRawValue());
12329         }
12330     },
12331     
12332     onSearchFieldClick : function(e)
12333     {
12334         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12335             this.onTickableFooterButtonClick(e, false, false);
12336             return;
12337         }
12338         
12339         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12340             return;
12341         }
12342         
12343         this.page = 0;
12344         this.loadNext = false;
12345         this.hasFocus = true;
12346         
12347         if(this.triggerAction == 'all') {
12348             this.doQuery(this.allQuery, true);
12349         } else {
12350             this.doQuery(this.getRawValue());
12351         }
12352     },
12353     
12354     listKeyPress : function(e)
12355     {
12356         //Roo.log('listkeypress');
12357         // scroll to first matching element based on key pres..
12358         if (e.isSpecialKey()) {
12359             return false;
12360         }
12361         var k = String.fromCharCode(e.getKey()).toUpperCase();
12362         //Roo.log(k);
12363         var match  = false;
12364         var csel = this.view.getSelectedNodes();
12365         var cselitem = false;
12366         if (csel.length) {
12367             var ix = this.view.indexOf(csel[0]);
12368             cselitem  = this.store.getAt(ix);
12369             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12370                 cselitem = false;
12371             }
12372             
12373         }
12374         
12375         this.store.each(function(v) { 
12376             if (cselitem) {
12377                 // start at existing selection.
12378                 if (cselitem.id == v.id) {
12379                     cselitem = false;
12380                 }
12381                 return true;
12382             }
12383                 
12384             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12385                 match = this.store.indexOf(v);
12386                 return false;
12387             }
12388             return true;
12389         }, this);
12390         
12391         if (match === false) {
12392             return true; // no more action?
12393         }
12394         // scroll to?
12395         this.view.select(match);
12396         var sn = Roo.get(this.view.getSelectedNodes()[0])
12397         sn.scrollIntoView(sn.dom.parentNode, false);
12398     },
12399     
12400     onViewScroll : function(e, t){
12401         
12402         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){
12403             return;
12404         }
12405         
12406         this.hasQuery = true;
12407         
12408         this.loading = this.list.select('.loading', true).first();
12409         
12410         if(this.loading === null){
12411             this.list.createChild({
12412                 tag: 'div',
12413                 cls: 'loading select2-more-results select2-active',
12414                 html: 'Loading more results...'
12415             })
12416             
12417             this.loading = this.list.select('.loading', true).first();
12418             
12419             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12420             
12421             this.loading.hide();
12422         }
12423         
12424         this.loading.show();
12425         
12426         var _combo = this;
12427         
12428         this.page++;
12429         this.loadNext = true;
12430         
12431         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12432         
12433         return;
12434     },
12435     
12436     addItem : function(o)
12437     {   
12438         var dv = ''; // display value
12439         
12440         if (this.displayField) {
12441             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12442         } else {
12443             // this is an error condition!!!
12444             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12445         }
12446         
12447         if(!dv.length){
12448             return;
12449         }
12450         
12451         var choice = this.choices.createChild({
12452             tag: 'li',
12453             cls: 'select2-search-choice',
12454             cn: [
12455                 {
12456                     tag: 'div',
12457                     html: dv
12458                 },
12459                 {
12460                     tag: 'a',
12461                     href: '#',
12462                     cls: 'select2-search-choice-close',
12463                     tabindex: '-1'
12464                 }
12465             ]
12466             
12467         }, this.searchField);
12468         
12469         var close = choice.select('a.select2-search-choice-close', true).first()
12470         
12471         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12472         
12473         this.item.push(o);
12474         
12475         this.lastData = o;
12476         
12477         this.syncValue();
12478         
12479         this.inputEl().dom.value = '';
12480         
12481         this.validate();
12482     },
12483     
12484     onRemoveItem : function(e, _self, o)
12485     {
12486         e.preventDefault();
12487         
12488         this.lastItem = Roo.apply([], this.item);
12489         
12490         var index = this.item.indexOf(o.data) * 1;
12491         
12492         if( index < 0){
12493             Roo.log('not this item?!');
12494             return;
12495         }
12496         
12497         this.item.splice(index, 1);
12498         o.item.remove();
12499         
12500         this.syncValue();
12501         
12502         this.fireEvent('remove', this, e);
12503         
12504         this.validate();
12505         
12506     },
12507     
12508     syncValue : function()
12509     {
12510         if(!this.item.length){
12511             this.clearValue();
12512             return;
12513         }
12514             
12515         var value = [];
12516         var _this = this;
12517         Roo.each(this.item, function(i){
12518             if(_this.valueField){
12519                 value.push(i[_this.valueField]);
12520                 return;
12521             }
12522
12523             value.push(i);
12524         });
12525
12526         this.value = value.join(',');
12527
12528         if(this.hiddenField){
12529             this.hiddenField.dom.value = this.value;
12530         }
12531         
12532         this.store.fireEvent("datachanged", this.store);
12533     },
12534     
12535     clearItem : function()
12536     {
12537         if(!this.multiple){
12538             return;
12539         }
12540         
12541         this.item = [];
12542         
12543         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12544            c.remove();
12545         });
12546         
12547         this.syncValue();
12548         
12549         this.validate();
12550     },
12551     
12552     inputEl: function ()
12553     {
12554         if(this.tickable){
12555             return this.searchField;
12556         }
12557         return this.el.select('input.form-control',true).first();
12558     },
12559     
12560     
12561     onTickableFooterButtonClick : function(e, btn, el)
12562     {
12563         e.preventDefault();
12564         
12565         this.lastItem = Roo.apply([], this.item);
12566         
12567         if(btn && btn.name == 'cancel'){
12568             this.tickItems = Roo.apply([], this.item);
12569             this.collapse();
12570             return;
12571         }
12572         
12573         this.clearItem();
12574         
12575         var _this = this;
12576         
12577         Roo.each(this.tickItems, function(o){
12578             _this.addItem(o);
12579         });
12580         
12581         this.collapse();
12582         
12583     },
12584     
12585     validate : function()
12586     {
12587         var v = this.getRawValue();
12588         
12589         if(this.multiple){
12590             v = this.getValue();
12591         }
12592         
12593         if(this.disabled || this.allowBlank || v.length){
12594             this.markValid();
12595             return true;
12596         }
12597         
12598         this.markInvalid();
12599         return false;
12600     },
12601     
12602     tickableInputEl : function()
12603     {
12604         if(!this.tickable || !this.editable){
12605             return this.inputEl();
12606         }
12607         
12608         return this.inputEl().select('.select2-search-field-input', true).first();
12609     }
12610     
12611     
12612
12613     /** 
12614     * @cfg {Boolean} grow 
12615     * @hide 
12616     */
12617     /** 
12618     * @cfg {Number} growMin 
12619     * @hide 
12620     */
12621     /** 
12622     * @cfg {Number} growMax 
12623     * @hide 
12624     */
12625     /**
12626      * @hide
12627      * @method autoSize
12628      */
12629 });
12630 /*
12631  * Based on:
12632  * Ext JS Library 1.1.1
12633  * Copyright(c) 2006-2007, Ext JS, LLC.
12634  *
12635  * Originally Released Under LGPL - original licence link has changed is not relivant.
12636  *
12637  * Fork - LGPL
12638  * <script type="text/javascript">
12639  */
12640
12641 /**
12642  * @class Roo.View
12643  * @extends Roo.util.Observable
12644  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12645  * This class also supports single and multi selection modes. <br>
12646  * Create a data model bound view:
12647  <pre><code>
12648  var store = new Roo.data.Store(...);
12649
12650  var view = new Roo.View({
12651     el : "my-element",
12652     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12653  
12654     singleSelect: true,
12655     selectedClass: "ydataview-selected",
12656     store: store
12657  });
12658
12659  // listen for node click?
12660  view.on("click", function(vw, index, node, e){
12661  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12662  });
12663
12664  // load XML data
12665  dataModel.load("foobar.xml");
12666  </code></pre>
12667  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12668  * <br><br>
12669  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12670  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12671  * 
12672  * Note: old style constructor is still suported (container, template, config)
12673  * 
12674  * @constructor
12675  * Create a new View
12676  * @param {Object} config The config object
12677  * 
12678  */
12679 Roo.View = function(config, depreciated_tpl, depreciated_config){
12680     
12681     this.parent = false;
12682     
12683     if (typeof(depreciated_tpl) == 'undefined') {
12684         // new way.. - universal constructor.
12685         Roo.apply(this, config);
12686         this.el  = Roo.get(this.el);
12687     } else {
12688         // old format..
12689         this.el  = Roo.get(config);
12690         this.tpl = depreciated_tpl;
12691         Roo.apply(this, depreciated_config);
12692     }
12693     this.wrapEl  = this.el.wrap().wrap();
12694     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12695     
12696     
12697     if(typeof(this.tpl) == "string"){
12698         this.tpl = new Roo.Template(this.tpl);
12699     } else {
12700         // support xtype ctors..
12701         this.tpl = new Roo.factory(this.tpl, Roo);
12702     }
12703     
12704     
12705     this.tpl.compile();
12706     
12707     /** @private */
12708     this.addEvents({
12709         /**
12710          * @event beforeclick
12711          * Fires before a click is processed. Returns false to cancel the default action.
12712          * @param {Roo.View} this
12713          * @param {Number} index The index of the target node
12714          * @param {HTMLElement} node The target node
12715          * @param {Roo.EventObject} e The raw event object
12716          */
12717             "beforeclick" : true,
12718         /**
12719          * @event click
12720          * Fires when a template node is clicked.
12721          * @param {Roo.View} this
12722          * @param {Number} index The index of the target node
12723          * @param {HTMLElement} node The target node
12724          * @param {Roo.EventObject} e The raw event object
12725          */
12726             "click" : true,
12727         /**
12728          * @event dblclick
12729          * Fires when a template node is double clicked.
12730          * @param {Roo.View} this
12731          * @param {Number} index The index of the target node
12732          * @param {HTMLElement} node The target node
12733          * @param {Roo.EventObject} e The raw event object
12734          */
12735             "dblclick" : true,
12736         /**
12737          * @event contextmenu
12738          * Fires when a template node is right clicked.
12739          * @param {Roo.View} this
12740          * @param {Number} index The index of the target node
12741          * @param {HTMLElement} node The target node
12742          * @param {Roo.EventObject} e The raw event object
12743          */
12744             "contextmenu" : true,
12745         /**
12746          * @event selectionchange
12747          * Fires when the selected nodes change.
12748          * @param {Roo.View} this
12749          * @param {Array} selections Array of the selected nodes
12750          */
12751             "selectionchange" : true,
12752     
12753         /**
12754          * @event beforeselect
12755          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12756          * @param {Roo.View} this
12757          * @param {HTMLElement} node The node to be selected
12758          * @param {Array} selections Array of currently selected nodes
12759          */
12760             "beforeselect" : true,
12761         /**
12762          * @event preparedata
12763          * Fires on every row to render, to allow you to change the data.
12764          * @param {Roo.View} this
12765          * @param {Object} data to be rendered (change this)
12766          */
12767           "preparedata" : true
12768           
12769           
12770         });
12771
12772
12773
12774     this.el.on({
12775         "click": this.onClick,
12776         "dblclick": this.onDblClick,
12777         "contextmenu": this.onContextMenu,
12778         scope:this
12779     });
12780
12781     this.selections = [];
12782     this.nodes = [];
12783     this.cmp = new Roo.CompositeElementLite([]);
12784     if(this.store){
12785         this.store = Roo.factory(this.store, Roo.data);
12786         this.setStore(this.store, true);
12787     }
12788     
12789     if ( this.footer && this.footer.xtype) {
12790            
12791          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12792         
12793         this.footer.dataSource = this.store
12794         this.footer.container = fctr;
12795         this.footer = Roo.factory(this.footer, Roo);
12796         fctr.insertFirst(this.el);
12797         
12798         // this is a bit insane - as the paging toolbar seems to detach the el..
12799 //        dom.parentNode.parentNode.parentNode
12800          // they get detached?
12801     }
12802     
12803     
12804     Roo.View.superclass.constructor.call(this);
12805     
12806     
12807 };
12808
12809 Roo.extend(Roo.View, Roo.util.Observable, {
12810     
12811      /**
12812      * @cfg {Roo.data.Store} store Data store to load data from.
12813      */
12814     store : false,
12815     
12816     /**
12817      * @cfg {String|Roo.Element} el The container element.
12818      */
12819     el : '',
12820     
12821     /**
12822      * @cfg {String|Roo.Template} tpl The template used by this View 
12823      */
12824     tpl : false,
12825     /**
12826      * @cfg {String} dataName the named area of the template to use as the data area
12827      *                          Works with domtemplates roo-name="name"
12828      */
12829     dataName: false,
12830     /**
12831      * @cfg {String} selectedClass The css class to add to selected nodes
12832      */
12833     selectedClass : "x-view-selected",
12834      /**
12835      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12836      */
12837     emptyText : "",
12838     
12839     /**
12840      * @cfg {String} text to display on mask (default Loading)
12841      */
12842     mask : false,
12843     /**
12844      * @cfg {Boolean} multiSelect Allow multiple selection
12845      */
12846     multiSelect : false,
12847     /**
12848      * @cfg {Boolean} singleSelect Allow single selection
12849      */
12850     singleSelect:  false,
12851     
12852     /**
12853      * @cfg {Boolean} toggleSelect - selecting 
12854      */
12855     toggleSelect : false,
12856     
12857     /**
12858      * @cfg {Boolean} tickable - selecting 
12859      */
12860     tickable : false,
12861     
12862     /**
12863      * Returns the element this view is bound to.
12864      * @return {Roo.Element}
12865      */
12866     getEl : function(){
12867         return this.wrapEl;
12868     },
12869     
12870     
12871
12872     /**
12873      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12874      */
12875     refresh : function(){
12876         //Roo.log('refresh');
12877         var t = this.tpl;
12878         
12879         // if we are using something like 'domtemplate', then
12880         // the what gets used is:
12881         // t.applySubtemplate(NAME, data, wrapping data..)
12882         // the outer template then get' applied with
12883         //     the store 'extra data'
12884         // and the body get's added to the
12885         //      roo-name="data" node?
12886         //      <span class='roo-tpl-{name}'></span> ?????
12887         
12888         
12889         
12890         this.clearSelections();
12891         this.el.update("");
12892         var html = [];
12893         var records = this.store.getRange();
12894         if(records.length < 1) {
12895             
12896             // is this valid??  = should it render a template??
12897             
12898             this.el.update(this.emptyText);
12899             return;
12900         }
12901         var el = this.el;
12902         if (this.dataName) {
12903             this.el.update(t.apply(this.store.meta)); //????
12904             el = this.el.child('.roo-tpl-' + this.dataName);
12905         }
12906         
12907         for(var i = 0, len = records.length; i < len; i++){
12908             var data = this.prepareData(records[i].data, i, records[i]);
12909             this.fireEvent("preparedata", this, data, i, records[i]);
12910             
12911             var d = Roo.apply({}, data);
12912             
12913             if(this.tickable){
12914                 Roo.apply(d, {'roo-id' : Roo.id()});
12915                 
12916                 var _this = this;
12917             
12918                 Roo.each(this.parent.item, function(item){
12919                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12920                         return;
12921                     }
12922                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12923                 });
12924             }
12925             
12926             html[html.length] = Roo.util.Format.trim(
12927                 this.dataName ?
12928                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12929                     t.apply(d)
12930             );
12931         }
12932         
12933         
12934         
12935         el.update(html.join(""));
12936         this.nodes = el.dom.childNodes;
12937         this.updateIndexes(0);
12938     },
12939     
12940
12941     /**
12942      * Function to override to reformat the data that is sent to
12943      * the template for each node.
12944      * DEPRICATED - use the preparedata event handler.
12945      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12946      * a JSON object for an UpdateManager bound view).
12947      */
12948     prepareData : function(data, index, record)
12949     {
12950         this.fireEvent("preparedata", this, data, index, record);
12951         return data;
12952     },
12953
12954     onUpdate : function(ds, record){
12955         // Roo.log('on update');   
12956         this.clearSelections();
12957         var index = this.store.indexOf(record);
12958         var n = this.nodes[index];
12959         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12960         n.parentNode.removeChild(n);
12961         this.updateIndexes(index, index);
12962     },
12963
12964     
12965     
12966 // --------- FIXME     
12967     onAdd : function(ds, records, index)
12968     {
12969         //Roo.log(['on Add', ds, records, index] );        
12970         this.clearSelections();
12971         if(this.nodes.length == 0){
12972             this.refresh();
12973             return;
12974         }
12975         var n = this.nodes[index];
12976         for(var i = 0, len = records.length; i < len; i++){
12977             var d = this.prepareData(records[i].data, i, records[i]);
12978             if(n){
12979                 this.tpl.insertBefore(n, d);
12980             }else{
12981                 
12982                 this.tpl.append(this.el, d);
12983             }
12984         }
12985         this.updateIndexes(index);
12986     },
12987
12988     onRemove : function(ds, record, index){
12989        // Roo.log('onRemove');
12990         this.clearSelections();
12991         var el = this.dataName  ?
12992             this.el.child('.roo-tpl-' + this.dataName) :
12993             this.el; 
12994         
12995         el.dom.removeChild(this.nodes[index]);
12996         this.updateIndexes(index);
12997     },
12998
12999     /**
13000      * Refresh an individual node.
13001      * @param {Number} index
13002      */
13003     refreshNode : function(index){
13004         this.onUpdate(this.store, this.store.getAt(index));
13005     },
13006
13007     updateIndexes : function(startIndex, endIndex){
13008         var ns = this.nodes;
13009         startIndex = startIndex || 0;
13010         endIndex = endIndex || ns.length - 1;
13011         for(var i = startIndex; i <= endIndex; i++){
13012             ns[i].nodeIndex = i;
13013         }
13014     },
13015
13016     /**
13017      * Changes the data store this view uses and refresh the view.
13018      * @param {Store} store
13019      */
13020     setStore : function(store, initial){
13021         if(!initial && this.store){
13022             this.store.un("datachanged", this.refresh);
13023             this.store.un("add", this.onAdd);
13024             this.store.un("remove", this.onRemove);
13025             this.store.un("update", this.onUpdate);
13026             this.store.un("clear", this.refresh);
13027             this.store.un("beforeload", this.onBeforeLoad);
13028             this.store.un("load", this.onLoad);
13029             this.store.un("loadexception", this.onLoad);
13030         }
13031         if(store){
13032           
13033             store.on("datachanged", this.refresh, this);
13034             store.on("add", this.onAdd, this);
13035             store.on("remove", this.onRemove, this);
13036             store.on("update", this.onUpdate, this);
13037             store.on("clear", this.refresh, this);
13038             store.on("beforeload", this.onBeforeLoad, this);
13039             store.on("load", this.onLoad, this);
13040             store.on("loadexception", this.onLoad, this);
13041         }
13042         
13043         if(store){
13044             this.refresh();
13045         }
13046     },
13047     /**
13048      * onbeforeLoad - masks the loading area.
13049      *
13050      */
13051     onBeforeLoad : function(store,opts)
13052     {
13053          //Roo.log('onBeforeLoad');   
13054         if (!opts.add) {
13055             this.el.update("");
13056         }
13057         this.el.mask(this.mask ? this.mask : "Loading" ); 
13058     },
13059     onLoad : function ()
13060     {
13061         this.el.unmask();
13062     },
13063     
13064
13065     /**
13066      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13067      * @param {HTMLElement} node
13068      * @return {HTMLElement} The template node
13069      */
13070     findItemFromChild : function(node){
13071         var el = this.dataName  ?
13072             this.el.child('.roo-tpl-' + this.dataName,true) :
13073             this.el.dom; 
13074         
13075         if(!node || node.parentNode == el){
13076                     return node;
13077             }
13078             var p = node.parentNode;
13079             while(p && p != el){
13080             if(p.parentNode == el){
13081                 return p;
13082             }
13083             p = p.parentNode;
13084         }
13085             return null;
13086     },
13087
13088     /** @ignore */
13089     onClick : function(e){
13090         var item = this.findItemFromChild(e.getTarget());
13091         if(item){
13092             var index = this.indexOf(item);
13093             if(this.onItemClick(item, index, e) !== false){
13094                 this.fireEvent("click", this, index, item, e);
13095             }
13096         }else{
13097             this.clearSelections();
13098         }
13099     },
13100
13101     /** @ignore */
13102     onContextMenu : function(e){
13103         var item = this.findItemFromChild(e.getTarget());
13104         if(item){
13105             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13106         }
13107     },
13108
13109     /** @ignore */
13110     onDblClick : function(e){
13111         var item = this.findItemFromChild(e.getTarget());
13112         if(item){
13113             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13114         }
13115     },
13116
13117     onItemClick : function(item, index, e)
13118     {
13119         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13120             return false;
13121         }
13122         if (this.toggleSelect) {
13123             var m = this.isSelected(item) ? 'unselect' : 'select';
13124             //Roo.log(m);
13125             var _t = this;
13126             _t[m](item, true, false);
13127             return true;
13128         }
13129         if(this.multiSelect || this.singleSelect){
13130             if(this.multiSelect && e.shiftKey && this.lastSelection){
13131                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13132             }else{
13133                 this.select(item, this.multiSelect && e.ctrlKey);
13134                 this.lastSelection = item;
13135             }
13136             
13137             if(!this.tickable){
13138                 e.preventDefault();
13139             }
13140             
13141         }
13142         return true;
13143     },
13144
13145     /**
13146      * Get the number of selected nodes.
13147      * @return {Number}
13148      */
13149     getSelectionCount : function(){
13150         return this.selections.length;
13151     },
13152
13153     /**
13154      * Get the currently selected nodes.
13155      * @return {Array} An array of HTMLElements
13156      */
13157     getSelectedNodes : function(){
13158         return this.selections;
13159     },
13160
13161     /**
13162      * Get the indexes of the selected nodes.
13163      * @return {Array}
13164      */
13165     getSelectedIndexes : function(){
13166         var indexes = [], s = this.selections;
13167         for(var i = 0, len = s.length; i < len; i++){
13168             indexes.push(s[i].nodeIndex);
13169         }
13170         return indexes;
13171     },
13172
13173     /**
13174      * Clear all selections
13175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13176      */
13177     clearSelections : function(suppressEvent){
13178         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13179             this.cmp.elements = this.selections;
13180             this.cmp.removeClass(this.selectedClass);
13181             this.selections = [];
13182             if(!suppressEvent){
13183                 this.fireEvent("selectionchange", this, this.selections);
13184             }
13185         }
13186     },
13187
13188     /**
13189      * Returns true if the passed node is selected
13190      * @param {HTMLElement/Number} node The node or node index
13191      * @return {Boolean}
13192      */
13193     isSelected : function(node){
13194         var s = this.selections;
13195         if(s.length < 1){
13196             return false;
13197         }
13198         node = this.getNode(node);
13199         return s.indexOf(node) !== -1;
13200     },
13201
13202     /**
13203      * Selects nodes.
13204      * @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
13205      * @param {Boolean} keepExisting (optional) true to keep existing selections
13206      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13207      */
13208     select : function(nodeInfo, keepExisting, suppressEvent){
13209         if(nodeInfo instanceof Array){
13210             if(!keepExisting){
13211                 this.clearSelections(true);
13212             }
13213             for(var i = 0, len = nodeInfo.length; i < len; i++){
13214                 this.select(nodeInfo[i], true, true);
13215             }
13216             return;
13217         } 
13218         var node = this.getNode(nodeInfo);
13219         if(!node || this.isSelected(node)){
13220             return; // already selected.
13221         }
13222         if(!keepExisting){
13223             this.clearSelections(true);
13224         }
13225         
13226         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13227             Roo.fly(node).addClass(this.selectedClass);
13228             this.selections.push(node);
13229             if(!suppressEvent){
13230                 this.fireEvent("selectionchange", this, this.selections);
13231             }
13232         }
13233         
13234         
13235     },
13236       /**
13237      * Unselects nodes.
13238      * @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
13239      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13240      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13241      */
13242     unselect : function(nodeInfo, keepExisting, suppressEvent)
13243     {
13244         if(nodeInfo instanceof Array){
13245             Roo.each(this.selections, function(s) {
13246                 this.unselect(s, nodeInfo);
13247             }, this);
13248             return;
13249         }
13250         var node = this.getNode(nodeInfo);
13251         if(!node || !this.isSelected(node)){
13252             //Roo.log("not selected");
13253             return; // not selected.
13254         }
13255         // fireevent???
13256         var ns = [];
13257         Roo.each(this.selections, function(s) {
13258             if (s == node ) {
13259                 Roo.fly(node).removeClass(this.selectedClass);
13260
13261                 return;
13262             }
13263             ns.push(s);
13264         },this);
13265         
13266         this.selections= ns;
13267         this.fireEvent("selectionchange", this, this.selections);
13268     },
13269
13270     /**
13271      * Gets a template node.
13272      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13273      * @return {HTMLElement} The node or null if it wasn't found
13274      */
13275     getNode : function(nodeInfo){
13276         if(typeof nodeInfo == "string"){
13277             return document.getElementById(nodeInfo);
13278         }else if(typeof nodeInfo == "number"){
13279             return this.nodes[nodeInfo];
13280         }
13281         return nodeInfo;
13282     },
13283
13284     /**
13285      * Gets a range template nodes.
13286      * @param {Number} startIndex
13287      * @param {Number} endIndex
13288      * @return {Array} An array of nodes
13289      */
13290     getNodes : function(start, end){
13291         var ns = this.nodes;
13292         start = start || 0;
13293         end = typeof end == "undefined" ? ns.length - 1 : end;
13294         var nodes = [];
13295         if(start <= end){
13296             for(var i = start; i <= end; i++){
13297                 nodes.push(ns[i]);
13298             }
13299         } else{
13300             for(var i = start; i >= end; i--){
13301                 nodes.push(ns[i]);
13302             }
13303         }
13304         return nodes;
13305     },
13306
13307     /**
13308      * Finds the index of the passed node
13309      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13310      * @return {Number} The index of the node or -1
13311      */
13312     indexOf : function(node){
13313         node = this.getNode(node);
13314         if(typeof node.nodeIndex == "number"){
13315             return node.nodeIndex;
13316         }
13317         var ns = this.nodes;
13318         for(var i = 0, len = ns.length; i < len; i++){
13319             if(ns[i] == node){
13320                 return i;
13321             }
13322         }
13323         return -1;
13324     }
13325 });
13326 /*
13327  * - LGPL
13328  *
13329  * based on jquery fullcalendar
13330  * 
13331  */
13332
13333 Roo.bootstrap = Roo.bootstrap || {};
13334 /**
13335  * @class Roo.bootstrap.Calendar
13336  * @extends Roo.bootstrap.Component
13337  * Bootstrap Calendar class
13338  * @cfg {Boolean} loadMask (true|false) default false
13339  * @cfg {Object} header generate the user specific header of the calendar, default false
13340
13341  * @constructor
13342  * Create a new Container
13343  * @param {Object} config The config object
13344  */
13345
13346
13347
13348 Roo.bootstrap.Calendar = function(config){
13349     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13350      this.addEvents({
13351         /**
13352              * @event select
13353              * Fires when a date is selected
13354              * @param {DatePicker} this
13355              * @param {Date} date The selected date
13356              */
13357         'select': true,
13358         /**
13359              * @event monthchange
13360              * Fires when the displayed month changes 
13361              * @param {DatePicker} this
13362              * @param {Date} date The selected month
13363              */
13364         'monthchange': true,
13365         /**
13366              * @event evententer
13367              * Fires when mouse over an event
13368              * @param {Calendar} this
13369              * @param {event} Event
13370              */
13371         'evententer': true,
13372         /**
13373              * @event eventleave
13374              * Fires when the mouse leaves an
13375              * @param {Calendar} this
13376              * @param {event}
13377              */
13378         'eventleave': true,
13379         /**
13380              * @event eventclick
13381              * Fires when the mouse click an
13382              * @param {Calendar} this
13383              * @param {event}
13384              */
13385         'eventclick': true
13386         
13387     });
13388
13389 };
13390
13391 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13392     
13393      /**
13394      * @cfg {Number} startDay
13395      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13396      */
13397     startDay : 0,
13398     
13399     loadMask : false,
13400     
13401     header : false,
13402       
13403     getAutoCreate : function(){
13404         
13405         
13406         var fc_button = function(name, corner, style, content ) {
13407             return Roo.apply({},{
13408                 tag : 'span',
13409                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13410                          (corner.length ?
13411                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13412                             ''
13413                         ),
13414                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13415                 unselectable: 'on'
13416             });
13417         };
13418         
13419         var header = {};
13420         
13421         if(!this.header){
13422             header = {
13423                 tag : 'table',
13424                 cls : 'fc-header',
13425                 style : 'width:100%',
13426                 cn : [
13427                     {
13428                         tag: 'tr',
13429                         cn : [
13430                             {
13431                                 tag : 'td',
13432                                 cls : 'fc-header-left',
13433                                 cn : [
13434                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13435                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13436                                     { tag: 'span', cls: 'fc-header-space' },
13437                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13438
13439
13440                                 ]
13441                             },
13442
13443                             {
13444                                 tag : 'td',
13445                                 cls : 'fc-header-center',
13446                                 cn : [
13447                                     {
13448                                         tag: 'span',
13449                                         cls: 'fc-header-title',
13450                                         cn : {
13451                                             tag: 'H2',
13452                                             html : 'month / year'
13453                                         }
13454                                     }
13455
13456                                 ]
13457                             },
13458                             {
13459                                 tag : 'td',
13460                                 cls : 'fc-header-right',
13461                                 cn : [
13462                               /*      fc_button('month', 'left', '', 'month' ),
13463                                     fc_button('week', '', '', 'week' ),
13464                                     fc_button('day', 'right', '', 'day' )
13465                                 */    
13466
13467                                 ]
13468                             }
13469
13470                         ]
13471                     }
13472                 ]
13473             };
13474         }
13475         
13476         header = this.header;
13477         
13478        
13479         var cal_heads = function() {
13480             var ret = [];
13481             // fixme - handle this.
13482             
13483             for (var i =0; i < Date.dayNames.length; i++) {
13484                 var d = Date.dayNames[i];
13485                 ret.push({
13486                     tag: 'th',
13487                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13488                     html : d.substring(0,3)
13489                 });
13490                 
13491             }
13492             ret[0].cls += ' fc-first';
13493             ret[6].cls += ' fc-last';
13494             return ret;
13495         };
13496         var cal_cell = function(n) {
13497             return  {
13498                 tag: 'td',
13499                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13500                 cn : [
13501                     {
13502                         cn : [
13503                             {
13504                                 cls: 'fc-day-number',
13505                                 html: 'D'
13506                             },
13507                             {
13508                                 cls: 'fc-day-content',
13509                              
13510                                 cn : [
13511                                      {
13512                                         style: 'position: relative;' // height: 17px;
13513                                     }
13514                                 ]
13515                             }
13516                             
13517                             
13518                         ]
13519                     }
13520                 ]
13521                 
13522             }
13523         };
13524         var cal_rows = function() {
13525             
13526             var ret = [];
13527             for (var r = 0; r < 6; r++) {
13528                 var row= {
13529                     tag : 'tr',
13530                     cls : 'fc-week',
13531                     cn : []
13532                 };
13533                 
13534                 for (var i =0; i < Date.dayNames.length; i++) {
13535                     var d = Date.dayNames[i];
13536                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13537
13538                 }
13539                 row.cn[0].cls+=' fc-first';
13540                 row.cn[0].cn[0].style = 'min-height:90px';
13541                 row.cn[6].cls+=' fc-last';
13542                 ret.push(row);
13543                 
13544             }
13545             ret[0].cls += ' fc-first';
13546             ret[4].cls += ' fc-prev-last';
13547             ret[5].cls += ' fc-last';
13548             return ret;
13549             
13550         };
13551         
13552         var cal_table = {
13553             tag: 'table',
13554             cls: 'fc-border-separate',
13555             style : 'width:100%',
13556             cellspacing  : 0,
13557             cn : [
13558                 { 
13559                     tag: 'thead',
13560                     cn : [
13561                         { 
13562                             tag: 'tr',
13563                             cls : 'fc-first fc-last',
13564                             cn : cal_heads()
13565                         }
13566                     ]
13567                 },
13568                 { 
13569                     tag: 'tbody',
13570                     cn : cal_rows()
13571                 }
13572                   
13573             ]
13574         };
13575          
13576          var cfg = {
13577             cls : 'fc fc-ltr',
13578             cn : [
13579                 header,
13580                 {
13581                     cls : 'fc-content',
13582                     style : "position: relative;",
13583                     cn : [
13584                         {
13585                             cls : 'fc-view fc-view-month fc-grid',
13586                             style : 'position: relative',
13587                             unselectable : 'on',
13588                             cn : [
13589                                 {
13590                                     cls : 'fc-event-container',
13591                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13592                                 },
13593                                 cal_table
13594                             ]
13595                         }
13596                     ]
13597     
13598                 }
13599            ] 
13600             
13601         };
13602         
13603          
13604         
13605         return cfg;
13606     },
13607     
13608     
13609     initEvents : function()
13610     {
13611         if(!this.store){
13612             throw "can not find store for calendar";
13613         }
13614         
13615         var mark = {
13616             tag: "div",
13617             cls:"x-dlg-mask",
13618             style: "text-align:center",
13619             cn: [
13620                 {
13621                     tag: "div",
13622                     style: "background-color:white;width:50%;margin:250 auto",
13623                     cn: [
13624                         {
13625                             tag: "img",
13626                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13627                         },
13628                         {
13629                             tag: "span",
13630                             html: "Loading"
13631                         }
13632                         
13633                     ]
13634                 }
13635             ]
13636         }
13637         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13638         
13639         var size = this.el.select('.fc-content', true).first().getSize();
13640         this.maskEl.setSize(size.width, size.height);
13641         this.maskEl.enableDisplayMode("block");
13642         if(!this.loadMask){
13643             this.maskEl.hide();
13644         }
13645         
13646         this.store = Roo.factory(this.store, Roo.data);
13647         this.store.on('load', this.onLoad, this);
13648         this.store.on('beforeload', this.onBeforeLoad, this);
13649         
13650         this.resize();
13651         
13652         this.cells = this.el.select('.fc-day',true);
13653         //Roo.log(this.cells);
13654         this.textNodes = this.el.query('.fc-day-number');
13655         this.cells.addClassOnOver('fc-state-hover');
13656         
13657         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13658         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13659         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13660         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13661         
13662         this.on('monthchange', this.onMonthChange, this);
13663         
13664         this.update(new Date().clearTime());
13665     },
13666     
13667     resize : function() {
13668         var sz  = this.el.getSize();
13669         
13670         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13671         this.el.select('.fc-day-content div',true).setHeight(34);
13672     },
13673     
13674     
13675     // private
13676     showPrevMonth : function(e){
13677         this.update(this.activeDate.add("mo", -1));
13678     },
13679     showToday : function(e){
13680         this.update(new Date().clearTime());
13681     },
13682     // private
13683     showNextMonth : function(e){
13684         this.update(this.activeDate.add("mo", 1));
13685     },
13686
13687     // private
13688     showPrevYear : function(){
13689         this.update(this.activeDate.add("y", -1));
13690     },
13691
13692     // private
13693     showNextYear : function(){
13694         this.update(this.activeDate.add("y", 1));
13695     },
13696
13697     
13698    // private
13699     update : function(date)
13700     {
13701         var vd = this.activeDate;
13702         this.activeDate = date;
13703 //        if(vd && this.el){
13704 //            var t = date.getTime();
13705 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13706 //                Roo.log('using add remove');
13707 //                
13708 //                this.fireEvent('monthchange', this, date);
13709 //                
13710 //                this.cells.removeClass("fc-state-highlight");
13711 //                this.cells.each(function(c){
13712 //                   if(c.dateValue == t){
13713 //                       c.addClass("fc-state-highlight");
13714 //                       setTimeout(function(){
13715 //                            try{c.dom.firstChild.focus();}catch(e){}
13716 //                       }, 50);
13717 //                       return false;
13718 //                   }
13719 //                   return true;
13720 //                });
13721 //                return;
13722 //            }
13723 //        }
13724         
13725         var days = date.getDaysInMonth();
13726         
13727         var firstOfMonth = date.getFirstDateOfMonth();
13728         var startingPos = firstOfMonth.getDay()-this.startDay;
13729         
13730         if(startingPos < this.startDay){
13731             startingPos += 7;
13732         }
13733         
13734         var pm = date.add(Date.MONTH, -1);
13735         var prevStart = pm.getDaysInMonth()-startingPos;
13736 //        
13737         this.cells = this.el.select('.fc-day',true);
13738         this.textNodes = this.el.query('.fc-day-number');
13739         this.cells.addClassOnOver('fc-state-hover');
13740         
13741         var cells = this.cells.elements;
13742         var textEls = this.textNodes;
13743         
13744         Roo.each(cells, function(cell){
13745             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13746         });
13747         
13748         days += startingPos;
13749
13750         // convert everything to numbers so it's fast
13751         var day = 86400000;
13752         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13753         //Roo.log(d);
13754         //Roo.log(pm);
13755         //Roo.log(prevStart);
13756         
13757         var today = new Date().clearTime().getTime();
13758         var sel = date.clearTime().getTime();
13759         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13760         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13761         var ddMatch = this.disabledDatesRE;
13762         var ddText = this.disabledDatesText;
13763         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13764         var ddaysText = this.disabledDaysText;
13765         var format = this.format;
13766         
13767         var setCellClass = function(cal, cell){
13768             cell.row = 0;
13769             cell.events = [];
13770             cell.more = [];
13771             //Roo.log('set Cell Class');
13772             cell.title = "";
13773             var t = d.getTime();
13774             
13775             //Roo.log(d);
13776             
13777             cell.dateValue = t;
13778             if(t == today){
13779                 cell.className += " fc-today";
13780                 cell.className += " fc-state-highlight";
13781                 cell.title = cal.todayText;
13782             }
13783             if(t == sel){
13784                 // disable highlight in other month..
13785                 //cell.className += " fc-state-highlight";
13786                 
13787             }
13788             // disabling
13789             if(t < min) {
13790                 cell.className = " fc-state-disabled";
13791                 cell.title = cal.minText;
13792                 return;
13793             }
13794             if(t > max) {
13795                 cell.className = " fc-state-disabled";
13796                 cell.title = cal.maxText;
13797                 return;
13798             }
13799             if(ddays){
13800                 if(ddays.indexOf(d.getDay()) != -1){
13801                     cell.title = ddaysText;
13802                     cell.className = " fc-state-disabled";
13803                 }
13804             }
13805             if(ddMatch && format){
13806                 var fvalue = d.dateFormat(format);
13807                 if(ddMatch.test(fvalue)){
13808                     cell.title = ddText.replace("%0", fvalue);
13809                     cell.className = " fc-state-disabled";
13810                 }
13811             }
13812             
13813             if (!cell.initialClassName) {
13814                 cell.initialClassName = cell.dom.className;
13815             }
13816             
13817             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13818         };
13819
13820         var i = 0;
13821         
13822         for(; i < startingPos; i++) {
13823             textEls[i].innerHTML = (++prevStart);
13824             d.setDate(d.getDate()+1);
13825             
13826             cells[i].className = "fc-past fc-other-month";
13827             setCellClass(this, cells[i]);
13828         }
13829         
13830         var intDay = 0;
13831         
13832         for(; i < days; i++){
13833             intDay = i - startingPos + 1;
13834             textEls[i].innerHTML = (intDay);
13835             d.setDate(d.getDate()+1);
13836             
13837             cells[i].className = ''; // "x-date-active";
13838             setCellClass(this, cells[i]);
13839         }
13840         var extraDays = 0;
13841         
13842         for(; i < 42; i++) {
13843             textEls[i].innerHTML = (++extraDays);
13844             d.setDate(d.getDate()+1);
13845             
13846             cells[i].className = "fc-future fc-other-month";
13847             setCellClass(this, cells[i]);
13848         }
13849         
13850         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13851         
13852         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13853         
13854         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13855         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13856         
13857         if(totalRows != 6){
13858             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13859             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13860         }
13861         
13862         this.fireEvent('monthchange', this, date);
13863         
13864         
13865         /*
13866         if(!this.internalRender){
13867             var main = this.el.dom.firstChild;
13868             var w = main.offsetWidth;
13869             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13870             Roo.fly(main).setWidth(w);
13871             this.internalRender = true;
13872             // opera does not respect the auto grow header center column
13873             // then, after it gets a width opera refuses to recalculate
13874             // without a second pass
13875             if(Roo.isOpera && !this.secondPass){
13876                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13877                 this.secondPass = true;
13878                 this.update.defer(10, this, [date]);
13879             }
13880         }
13881         */
13882         
13883     },
13884     
13885     findCell : function(dt) {
13886         dt = dt.clearTime().getTime();
13887         var ret = false;
13888         this.cells.each(function(c){
13889             //Roo.log("check " +c.dateValue + '?=' + dt);
13890             if(c.dateValue == dt){
13891                 ret = c;
13892                 return false;
13893             }
13894             return true;
13895         });
13896         
13897         return ret;
13898     },
13899     
13900     findCells : function(ev) {
13901         var s = ev.start.clone().clearTime().getTime();
13902        // Roo.log(s);
13903         var e= ev.end.clone().clearTime().getTime();
13904        // Roo.log(e);
13905         var ret = [];
13906         this.cells.each(function(c){
13907              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13908             
13909             if(c.dateValue > e){
13910                 return ;
13911             }
13912             if(c.dateValue < s){
13913                 return ;
13914             }
13915             ret.push(c);
13916         });
13917         
13918         return ret;    
13919     },
13920     
13921 //    findBestRow: function(cells)
13922 //    {
13923 //        var ret = 0;
13924 //        
13925 //        for (var i =0 ; i < cells.length;i++) {
13926 //            ret  = Math.max(cells[i].rows || 0,ret);
13927 //        }
13928 //        return ret;
13929 //        
13930 //    },
13931     
13932     
13933     addItem : function(ev)
13934     {
13935         // look for vertical location slot in
13936         var cells = this.findCells(ev);
13937         
13938 //        ev.row = this.findBestRow(cells);
13939         
13940         // work out the location.
13941         
13942         var crow = false;
13943         var rows = [];
13944         for(var i =0; i < cells.length; i++) {
13945             
13946             cells[i].row = cells[0].row;
13947             
13948             if(i == 0){
13949                 cells[i].row = cells[i].row + 1;
13950             }
13951             
13952             if (!crow) {
13953                 crow = {
13954                     start : cells[i],
13955                     end :  cells[i]
13956                 };
13957                 continue;
13958             }
13959             if (crow.start.getY() == cells[i].getY()) {
13960                 // on same row.
13961                 crow.end = cells[i];
13962                 continue;
13963             }
13964             // different row.
13965             rows.push(crow);
13966             crow = {
13967                 start: cells[i],
13968                 end : cells[i]
13969             };
13970             
13971         }
13972         
13973         rows.push(crow);
13974         ev.els = [];
13975         ev.rows = rows;
13976         ev.cells = cells;
13977         
13978         cells[0].events.push(ev);
13979         
13980         this.calevents.push(ev);
13981     },
13982     
13983     clearEvents: function() {
13984         
13985         if(!this.calevents){
13986             return;
13987         }
13988         
13989         Roo.each(this.cells.elements, function(c){
13990             c.row = 0;
13991             c.events = [];
13992             c.more = [];
13993         });
13994         
13995         Roo.each(this.calevents, function(e) {
13996             Roo.each(e.els, function(el) {
13997                 el.un('mouseenter' ,this.onEventEnter, this);
13998                 el.un('mouseleave' ,this.onEventLeave, this);
13999                 el.remove();
14000             },this);
14001         },this);
14002         
14003         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14004             e.remove();
14005         });
14006         
14007     },
14008     
14009     renderEvents: function()
14010     {   
14011         var _this = this;
14012         
14013         this.cells.each(function(c) {
14014             
14015             if(c.row < 5){
14016                 return;
14017             }
14018             
14019             var ev = c.events;
14020             
14021             var r = 4;
14022             if(c.row != c.events.length){
14023                 r = 4 - (4 - (c.row - c.events.length));
14024             }
14025             
14026             c.events = ev.slice(0, r);
14027             c.more = ev.slice(r);
14028             
14029             if(c.more.length && c.more.length == 1){
14030                 c.events.push(c.more.pop());
14031             }
14032             
14033             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14034             
14035         });
14036             
14037         this.cells.each(function(c) {
14038             
14039             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14040             
14041             
14042             for (var e = 0; e < c.events.length; e++){
14043                 var ev = c.events[e];
14044                 var rows = ev.rows;
14045                 
14046                 for(var i = 0; i < rows.length; i++) {
14047                 
14048                     // how many rows should it span..
14049
14050                     var  cfg = {
14051                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14052                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14053
14054                         unselectable : "on",
14055                         cn : [
14056                             {
14057                                 cls: 'fc-event-inner',
14058                                 cn : [
14059     //                                {
14060     //                                  tag:'span',
14061     //                                  cls: 'fc-event-time',
14062     //                                  html : cells.length > 1 ? '' : ev.time
14063     //                                },
14064                                     {
14065                                       tag:'span',
14066                                       cls: 'fc-event-title',
14067                                       html : String.format('{0}', ev.title)
14068                                     }
14069
14070
14071                                 ]
14072                             },
14073                             {
14074                                 cls: 'ui-resizable-handle ui-resizable-e',
14075                                 html : '&nbsp;&nbsp;&nbsp'
14076                             }
14077
14078                         ]
14079                     };
14080
14081                     if (i == 0) {
14082                         cfg.cls += ' fc-event-start';
14083                     }
14084                     if ((i+1) == rows.length) {
14085                         cfg.cls += ' fc-event-end';
14086                     }
14087
14088                     var ctr = _this.el.select('.fc-event-container',true).first();
14089                     var cg = ctr.createChild(cfg);
14090
14091                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14092                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14093
14094                     var r = (c.more.length) ? 1 : 0;
14095                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14096                     cg.setWidth(ebox.right - sbox.x -2);
14097
14098                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14099                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14100                     cg.on('click', _this.onEventClick, _this, ev);
14101
14102                     ev.els.push(cg);
14103                     
14104                 }
14105                 
14106             }
14107             
14108             
14109             if(c.more.length){
14110                 var  cfg = {
14111                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14112                     style : 'position: absolute',
14113                     unselectable : "on",
14114                     cn : [
14115                         {
14116                             cls: 'fc-event-inner',
14117                             cn : [
14118                                 {
14119                                   tag:'span',
14120                                   cls: 'fc-event-title',
14121                                   html : 'More'
14122                                 }
14123
14124
14125                             ]
14126                         },
14127                         {
14128                             cls: 'ui-resizable-handle ui-resizable-e',
14129                             html : '&nbsp;&nbsp;&nbsp'
14130                         }
14131
14132                     ]
14133                 };
14134
14135                 var ctr = _this.el.select('.fc-event-container',true).first();
14136                 var cg = ctr.createChild(cfg);
14137
14138                 var sbox = c.select('.fc-day-content',true).first().getBox();
14139                 var ebox = c.select('.fc-day-content',true).first().getBox();
14140                 //Roo.log(cg);
14141                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14142                 cg.setWidth(ebox.right - sbox.x -2);
14143
14144                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14145                 
14146             }
14147             
14148         });
14149         
14150         
14151         
14152     },
14153     
14154     onEventEnter: function (e, el,event,d) {
14155         this.fireEvent('evententer', this, el, event);
14156     },
14157     
14158     onEventLeave: function (e, el,event,d) {
14159         this.fireEvent('eventleave', this, el, event);
14160     },
14161     
14162     onEventClick: function (e, el,event,d) {
14163         this.fireEvent('eventclick', this, el, event);
14164     },
14165     
14166     onMonthChange: function () {
14167         this.store.load();
14168     },
14169     
14170     onMoreEventClick: function(e, el, more)
14171     {
14172         var _this = this;
14173         
14174         this.calpopover.placement = 'right';
14175         this.calpopover.setTitle('More');
14176         
14177         this.calpopover.setContent('');
14178         
14179         var ctr = this.calpopover.el.select('.popover-content', true).first();
14180         
14181         Roo.each(more, function(m){
14182             var cfg = {
14183                 cls : 'fc-event-hori fc-event-draggable',
14184                 html : m.title
14185             }
14186             var cg = ctr.createChild(cfg);
14187             
14188             cg.on('click', _this.onEventClick, _this, m);
14189         });
14190         
14191         this.calpopover.show(el);
14192         
14193         
14194     },
14195     
14196     onLoad: function () 
14197     {   
14198         this.calevents = [];
14199         var cal = this;
14200         
14201         if(this.store.getCount() > 0){
14202             this.store.data.each(function(d){
14203                cal.addItem({
14204                     id : d.data.id,
14205                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14206                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14207                     time : d.data.start_time,
14208                     title : d.data.title,
14209                     description : d.data.description,
14210                     venue : d.data.venue
14211                 });
14212             });
14213         }
14214         
14215         this.renderEvents();
14216         
14217         if(this.calevents.length && this.loadMask){
14218             this.maskEl.hide();
14219         }
14220     },
14221     
14222     onBeforeLoad: function()
14223     {
14224         this.clearEvents();
14225         if(this.loadMask){
14226             this.maskEl.show();
14227         }
14228     }
14229 });
14230
14231  
14232  /*
14233  * - LGPL
14234  *
14235  * element
14236  * 
14237  */
14238
14239 /**
14240  * @class Roo.bootstrap.Popover
14241  * @extends Roo.bootstrap.Component
14242  * Bootstrap Popover class
14243  * @cfg {String} html contents of the popover   (or false to use children..)
14244  * @cfg {String} title of popover (or false to hide)
14245  * @cfg {String} placement how it is placed
14246  * @cfg {String} trigger click || hover (or false to trigger manually)
14247  * @cfg {String} over what (parent or false to trigger manually.)
14248  * @cfg {Number} delay - delay before showing
14249  
14250  * @constructor
14251  * Create a new Popover
14252  * @param {Object} config The config object
14253  */
14254
14255 Roo.bootstrap.Popover = function(config){
14256     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14257 };
14258
14259 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14260     
14261     title: 'Fill in a title',
14262     html: false,
14263     
14264     placement : 'right',
14265     trigger : 'hover', // hover
14266     
14267     delay : 0,
14268     
14269     over: 'parent',
14270     
14271     can_build_overlaid : false,
14272     
14273     getChildContainer : function()
14274     {
14275         return this.el.select('.popover-content',true).first();
14276     },
14277     
14278     getAutoCreate : function(){
14279          Roo.log('make popover?');
14280         var cfg = {
14281            cls : 'popover roo-dynamic',
14282            style: 'display:block',
14283            cn : [
14284                 {
14285                     cls : 'arrow'
14286                 },
14287                 {
14288                     cls : 'popover-inner',
14289                     cn : [
14290                         {
14291                             tag: 'h3',
14292                             cls: 'popover-title',
14293                             html : this.title
14294                         },
14295                         {
14296                             cls : 'popover-content',
14297                             html : this.html
14298                         }
14299                     ]
14300                     
14301                 }
14302            ]
14303         };
14304         
14305         return cfg;
14306     },
14307     setTitle: function(str)
14308     {
14309         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14310     },
14311     setContent: function(str)
14312     {
14313         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14314     },
14315     // as it get's added to the bottom of the page.
14316     onRender : function(ct, position)
14317     {
14318         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14319         if(!this.el){
14320             var cfg = Roo.apply({},  this.getAutoCreate());
14321             cfg.id = Roo.id();
14322             
14323             if (this.cls) {
14324                 cfg.cls += ' ' + this.cls;
14325             }
14326             if (this.style) {
14327                 cfg.style = this.style;
14328             }
14329             Roo.log("adding to ")
14330             this.el = Roo.get(document.body).createChild(cfg, position);
14331             Roo.log(this.el);
14332         }
14333         this.initEvents();
14334     },
14335     
14336     initEvents : function()
14337     {
14338         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14339         this.el.enableDisplayMode('block');
14340         this.el.hide();
14341         if (this.over === false) {
14342             return; 
14343         }
14344         if (this.triggers === false) {
14345             return;
14346         }
14347         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14348         var triggers = this.trigger ? this.trigger.split(' ') : [];
14349         Roo.each(triggers, function(trigger) {
14350         
14351             if (trigger == 'click') {
14352                 on_el.on('click', this.toggle, this);
14353             } else if (trigger != 'manual') {
14354                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14355                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14356       
14357                 on_el.on(eventIn  ,this.enter, this);
14358                 on_el.on(eventOut, this.leave, this);
14359             }
14360         }, this);
14361         
14362     },
14363     
14364     
14365     // private
14366     timeout : null,
14367     hoverState : null,
14368     
14369     toggle : function () {
14370         this.hoverState == 'in' ? this.leave() : this.enter();
14371     },
14372     
14373     enter : function () {
14374        
14375     
14376         clearTimeout(this.timeout);
14377     
14378         this.hoverState = 'in';
14379     
14380         if (!this.delay || !this.delay.show) {
14381             this.show();
14382             return;
14383         }
14384         var _t = this;
14385         this.timeout = setTimeout(function () {
14386             if (_t.hoverState == 'in') {
14387                 _t.show();
14388             }
14389         }, this.delay.show)
14390     },
14391     leave : function() {
14392         clearTimeout(this.timeout);
14393     
14394         this.hoverState = 'out';
14395     
14396         if (!this.delay || !this.delay.hide) {
14397             this.hide();
14398             return;
14399         }
14400         var _t = this;
14401         this.timeout = setTimeout(function () {
14402             if (_t.hoverState == 'out') {
14403                 _t.hide();
14404             }
14405         }, this.delay.hide)
14406     },
14407     
14408     show : function (on_el)
14409     {
14410         if (!on_el) {
14411             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14412         }
14413         // set content.
14414         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14415         if (this.html !== false) {
14416             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14417         }
14418         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14419         if (!this.title.length) {
14420             this.el.select('.popover-title',true).hide();
14421         }
14422         
14423         var placement = typeof this.placement == 'function' ?
14424             this.placement.call(this, this.el, on_el) :
14425             this.placement;
14426             
14427         var autoToken = /\s?auto?\s?/i;
14428         var autoPlace = autoToken.test(placement);
14429         if (autoPlace) {
14430             placement = placement.replace(autoToken, '') || 'top';
14431         }
14432         
14433         //this.el.detach()
14434         //this.el.setXY([0,0]);
14435         this.el.show();
14436         this.el.dom.style.display='block';
14437         this.el.addClass(placement);
14438         
14439         //this.el.appendTo(on_el);
14440         
14441         var p = this.getPosition();
14442         var box = this.el.getBox();
14443         
14444         if (autoPlace) {
14445             // fixme..
14446         }
14447         var align = Roo.bootstrap.Popover.alignment[placement];
14448         this.el.alignTo(on_el, align[0],align[1]);
14449         //var arrow = this.el.select('.arrow',true).first();
14450         //arrow.set(align[2], 
14451         
14452         this.el.addClass('in');
14453         this.hoverState = null;
14454         
14455         if (this.el.hasClass('fade')) {
14456             // fade it?
14457         }
14458         
14459     },
14460     hide : function()
14461     {
14462         this.el.setXY([0,0]);
14463         this.el.removeClass('in');
14464         this.el.hide();
14465         
14466     }
14467     
14468 });
14469
14470 Roo.bootstrap.Popover.alignment = {
14471     'left' : ['r-l', [-10,0], 'right'],
14472     'right' : ['l-r', [10,0], 'left'],
14473     'bottom' : ['t-b', [0,10], 'top'],
14474     'top' : [ 'b-t', [0,-10], 'bottom']
14475 };
14476
14477  /*
14478  * - LGPL
14479  *
14480  * Progress
14481  * 
14482  */
14483
14484 /**
14485  * @class Roo.bootstrap.Progress
14486  * @extends Roo.bootstrap.Component
14487  * Bootstrap Progress class
14488  * @cfg {Boolean} striped striped of the progress bar
14489  * @cfg {Boolean} active animated of the progress bar
14490  * 
14491  * 
14492  * @constructor
14493  * Create a new Progress
14494  * @param {Object} config The config object
14495  */
14496
14497 Roo.bootstrap.Progress = function(config){
14498     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14499 };
14500
14501 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14502     
14503     striped : false,
14504     active: false,
14505     
14506     getAutoCreate : function(){
14507         var cfg = {
14508             tag: 'div',
14509             cls: 'progress'
14510         };
14511         
14512         
14513         if(this.striped){
14514             cfg.cls += ' progress-striped';
14515         }
14516       
14517         if(this.active){
14518             cfg.cls += ' active';
14519         }
14520         
14521         
14522         return cfg;
14523     }
14524    
14525 });
14526
14527  
14528
14529  /*
14530  * - LGPL
14531  *
14532  * ProgressBar
14533  * 
14534  */
14535
14536 /**
14537  * @class Roo.bootstrap.ProgressBar
14538  * @extends Roo.bootstrap.Component
14539  * Bootstrap ProgressBar class
14540  * @cfg {Number} aria_valuenow aria-value now
14541  * @cfg {Number} aria_valuemin aria-value min
14542  * @cfg {Number} aria_valuemax aria-value max
14543  * @cfg {String} label label for the progress bar
14544  * @cfg {String} panel (success | info | warning | danger )
14545  * @cfg {String} role role of the progress bar
14546  * @cfg {String} sr_only text
14547  * 
14548  * 
14549  * @constructor
14550  * Create a new ProgressBar
14551  * @param {Object} config The config object
14552  */
14553
14554 Roo.bootstrap.ProgressBar = function(config){
14555     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14556 };
14557
14558 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14559     
14560     aria_valuenow : 0,
14561     aria_valuemin : 0,
14562     aria_valuemax : 100,
14563     label : false,
14564     panel : false,
14565     role : false,
14566     sr_only: false,
14567     
14568     getAutoCreate : function()
14569     {
14570         
14571         var cfg = {
14572             tag: 'div',
14573             cls: 'progress-bar',
14574             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14575         };
14576         
14577         if(this.sr_only){
14578             cfg.cn = {
14579                 tag: 'span',
14580                 cls: 'sr-only',
14581                 html: this.sr_only
14582             }
14583         }
14584         
14585         if(this.role){
14586             cfg.role = this.role;
14587         }
14588         
14589         if(this.aria_valuenow){
14590             cfg['aria-valuenow'] = this.aria_valuenow;
14591         }
14592         
14593         if(this.aria_valuemin){
14594             cfg['aria-valuemin'] = this.aria_valuemin;
14595         }
14596         
14597         if(this.aria_valuemax){
14598             cfg['aria-valuemax'] = this.aria_valuemax;
14599         }
14600         
14601         if(this.label && !this.sr_only){
14602             cfg.html = this.label;
14603         }
14604         
14605         if(this.panel){
14606             cfg.cls += ' progress-bar-' + this.panel;
14607         }
14608         
14609         return cfg;
14610     },
14611     
14612     update : function(aria_valuenow)
14613     {
14614         this.aria_valuenow = aria_valuenow;
14615         
14616         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14617     }
14618    
14619 });
14620
14621  
14622
14623  /*
14624  * - LGPL
14625  *
14626  * column
14627  * 
14628  */
14629
14630 /**
14631  * @class Roo.bootstrap.TabGroup
14632  * @extends Roo.bootstrap.Column
14633  * Bootstrap Column class
14634  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14635  * @cfg {Boolean} carousel true to make the group behave like a carousel
14636  * @cfg {Number} bullets show the panel pointer.. default 0
14637  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14638  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14639  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14640  * 
14641  * @constructor
14642  * Create a new TabGroup
14643  * @param {Object} config The config object
14644  */
14645
14646 Roo.bootstrap.TabGroup = function(config){
14647     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14648     if (!this.navId) {
14649         this.navId = Roo.id();
14650     }
14651     this.tabs = [];
14652     Roo.bootstrap.TabGroup.register(this);
14653     
14654 };
14655
14656 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14657     
14658     carousel : false,
14659     transition : false,
14660     bullets : 0,
14661     timer : 0,
14662     autoslide : false,
14663     slideFn : false,
14664     slideOnTouch : false,
14665     
14666     getAutoCreate : function()
14667     {
14668         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14669         
14670         cfg.cls += ' tab-content';
14671         
14672         Roo.log('get auto create...............');
14673         
14674         if (this.carousel) {
14675             cfg.cls += ' carousel slide';
14676             
14677             cfg.cn = [{
14678                cls : 'carousel-inner'
14679             }];
14680         
14681             if(this.bullets > 0 && !Roo.isTouch){
14682                 
14683                 var bullets = {
14684                     cls : 'carousel-bullets',
14685                     cn : []
14686                 };
14687                 
14688                 if(this.bullets_cls){
14689                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14690                 }
14691                 
14692                 for (var i = 0; i < this.bullets; i++){
14693                     bullets.cn.push({
14694                         cls : 'bullet bullet-' + i
14695                     });
14696                 }
14697                 
14698                 bullets.cn.push({
14699                     cls : 'clear'
14700                 });
14701                 
14702                 cfg.cn[0].cn = bullets;
14703             }
14704         }
14705         
14706         return cfg;
14707     },
14708     
14709     initEvents:  function()
14710     {
14711         Roo.log('-------- init events on tab group ---------');
14712         
14713         if(this.bullets > 0 && !Roo.isTouch){
14714             this.initBullet();
14715         }
14716         
14717         Roo.log(this);
14718         
14719         if(Roo.isTouch && this.slideOnTouch){
14720             this.el.on("touchstart", this.onTouchStart, this);
14721         }
14722         
14723         if(this.autoslide){
14724             var _this = this;
14725             
14726             this.slideFn = window.setInterval(function() {
14727                 _this.showPanelNext();
14728             }, this.timer);
14729         }
14730         
14731     },
14732     
14733     onTouchStart : function(e, el, o)
14734     {
14735         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14736             return;
14737         }
14738         
14739         this.showPanelNext();
14740     },
14741     
14742     getChildContainer : function()
14743     {
14744         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14745     },
14746     
14747     /**
14748     * register a Navigation item
14749     * @param {Roo.bootstrap.NavItem} the navitem to add
14750     */
14751     register : function(item)
14752     {
14753         this.tabs.push( item);
14754         item.navId = this.navId; // not really needed..
14755     
14756     },
14757     
14758     getActivePanel : function()
14759     {
14760         var r = false;
14761         Roo.each(this.tabs, function(t) {
14762             if (t.active) {
14763                 r = t;
14764                 return false;
14765             }
14766             return null;
14767         });
14768         return r;
14769         
14770     },
14771     getPanelByName : function(n)
14772     {
14773         var r = false;
14774         Roo.each(this.tabs, function(t) {
14775             if (t.tabId == n) {
14776                 r = t;
14777                 return false;
14778             }
14779             return null;
14780         });
14781         return r;
14782     },
14783     indexOfPanel : function(p)
14784     {
14785         var r = false;
14786         Roo.each(this.tabs, function(t,i) {
14787             if (t.tabId == p.tabId) {
14788                 r = i;
14789                 return false;
14790             }
14791             return null;
14792         });
14793         return r;
14794     },
14795     /**
14796      * show a specific panel
14797      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14798      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14799      */
14800     showPanel : function (pan)
14801     {
14802         if(this.transition){
14803             Roo.log("waiting for the transitionend");
14804             return;
14805         }
14806         
14807         if (typeof(pan) == 'number') {
14808             pan = this.tabs[pan];
14809         }
14810         if (typeof(pan) == 'string') {
14811             pan = this.getPanelByName(pan);
14812         }
14813         if (pan.tabId == this.getActivePanel().tabId) {
14814             return true;
14815         }
14816         var cur = this.getActivePanel();
14817         
14818         if (false === cur.fireEvent('beforedeactivate')) {
14819             return false;
14820         }
14821         
14822         if(this.bullets > 0 && !Roo.isTouch){
14823             this.setActiveBullet(this.indexOfPanel(pan));
14824         }
14825         
14826         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14827             
14828             this.transition = true;
14829             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14830             var lr = dir == 'next' ? 'left' : 'right';
14831             pan.el.addClass(dir); // or prev
14832             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14833             cur.el.addClass(lr); // or right
14834             pan.el.addClass(lr);
14835             
14836             var _this = this;
14837             cur.el.on('transitionend', function() {
14838                 Roo.log("trans end?");
14839                 
14840                 pan.el.removeClass([lr,dir]);
14841                 pan.setActive(true);
14842                 
14843                 cur.el.removeClass([lr]);
14844                 cur.setActive(false);
14845                 
14846                 _this.transition = false;
14847                 
14848             }, this, { single:  true } );
14849             
14850             return true;
14851         }
14852         
14853         cur.setActive(false);
14854         pan.setActive(true);
14855         
14856         return true;
14857         
14858     },
14859     showPanelNext : function()
14860     {
14861         var i = this.indexOfPanel(this.getActivePanel());
14862         
14863         if (i >= this.tabs.length - 1 && !this.autoslide) {
14864             return;
14865         }
14866         
14867         if (i >= this.tabs.length - 1 && this.autoslide) {
14868             i = -1;
14869         }
14870         
14871         this.showPanel(this.tabs[i+1]);
14872     },
14873     
14874     showPanelPrev : function()
14875     {
14876         var i = this.indexOfPanel(this.getActivePanel());
14877         
14878         if (i  < 1 && !this.autoslide) {
14879             return;
14880         }
14881         
14882         if (i < 1 && this.autoslide) {
14883             i = this.tabs.length;
14884         }
14885         
14886         this.showPanel(this.tabs[i-1]);
14887     },
14888     
14889     initBullet : function()
14890     {
14891         if(Roo.isTouch){
14892             return;
14893         }
14894         
14895         var _this = this;
14896         
14897         for (var i = 0; i < this.bullets; i++){
14898             var bullet = this.el.select('.bullet-' + i, true).first();
14899
14900             if(!bullet){
14901                 continue;
14902             }
14903
14904             bullet.on('click', (function(e, el, o, ii, t){
14905
14906                 e.preventDefault();
14907
14908                 _this.showPanel(ii);
14909
14910                 if(_this.autoslide && _this.slideFn){
14911                     clearInterval(_this.slideFn);
14912                     _this.slideFn = window.setInterval(function() {
14913                         _this.showPanelNext();
14914                     }, _this.timer);
14915                 }
14916
14917             }).createDelegate(this, [i, bullet], true));
14918         }
14919     },
14920     
14921     setActiveBullet : function(i)
14922     {
14923         if(Roo.isTouch){
14924             return;
14925         }
14926         
14927         Roo.each(this.el.select('.bullet', true).elements, function(el){
14928             el.removeClass('selected');
14929         });
14930
14931         var bullet = this.el.select('.bullet-' + i, true).first();
14932         
14933         if(!bullet){
14934             return;
14935         }
14936         
14937         bullet.addClass('selected');
14938     }
14939     
14940     
14941   
14942 });
14943
14944  
14945
14946  
14947  
14948 Roo.apply(Roo.bootstrap.TabGroup, {
14949     
14950     groups: {},
14951      /**
14952     * register a Navigation Group
14953     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14954     */
14955     register : function(navgrp)
14956     {
14957         this.groups[navgrp.navId] = navgrp;
14958         
14959     },
14960     /**
14961     * fetch a Navigation Group based on the navigation ID
14962     * if one does not exist , it will get created.
14963     * @param {string} the navgroup to add
14964     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14965     */
14966     get: function(navId) {
14967         if (typeof(this.groups[navId]) == 'undefined') {
14968             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14969         }
14970         return this.groups[navId] ;
14971     }
14972     
14973     
14974     
14975 });
14976
14977  /*
14978  * - LGPL
14979  *
14980  * TabPanel
14981  * 
14982  */
14983
14984 /**
14985  * @class Roo.bootstrap.TabPanel
14986  * @extends Roo.bootstrap.Component
14987  * Bootstrap TabPanel class
14988  * @cfg {Boolean} active panel active
14989  * @cfg {String} html panel content
14990  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14991  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14992  * 
14993  * 
14994  * @constructor
14995  * Create a new TabPanel
14996  * @param {Object} config The config object
14997  */
14998
14999 Roo.bootstrap.TabPanel = function(config){
15000     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15001     this.addEvents({
15002         /**
15003              * @event changed
15004              * Fires when the active status changes
15005              * @param {Roo.bootstrap.TabPanel} this
15006              * @param {Boolean} state the new state
15007             
15008          */
15009         'changed': true,
15010         /**
15011              * @event beforedeactivate
15012              * Fires before a tab is de-activated - can be used to do validation on a form.
15013              * @param {Roo.bootstrap.TabPanel} this
15014              * @return {Boolean} false if there is an error
15015             
15016          */
15017         'beforedeactivate': true
15018      });
15019     
15020     this.tabId = this.tabId || Roo.id();
15021   
15022 };
15023
15024 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15025     
15026     active: false,
15027     html: false,
15028     tabId: false,
15029     navId : false,
15030     
15031     getAutoCreate : function(){
15032         var cfg = {
15033             tag: 'div',
15034             // item is needed for carousel - not sure if it has any effect otherwise
15035             cls: 'tab-pane item',
15036             html: this.html || ''
15037         };
15038         
15039         if(this.active){
15040             cfg.cls += ' active';
15041         }
15042         
15043         if(this.tabId){
15044             cfg.tabId = this.tabId;
15045         }
15046         
15047         
15048         return cfg;
15049     },
15050     
15051     initEvents:  function()
15052     {
15053         Roo.log('-------- init events on tab panel ---------');
15054         
15055         var p = this.parent();
15056         this.navId = this.navId || p.navId;
15057         
15058         if (typeof(this.navId) != 'undefined') {
15059             // not really needed.. but just in case.. parent should be a NavGroup.
15060             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15061             Roo.log(['register', tg, this]);
15062             tg.register(this);
15063             
15064             var i = tg.tabs.length - 1;
15065             
15066             if(this.active && tg.bullets > 0 && i < tg.bullets){
15067                 tg.setActiveBullet(i);
15068             }
15069         }
15070         
15071     },
15072     
15073     
15074     onRender : function(ct, position)
15075     {
15076        // Roo.log("Call onRender: " + this.xtype);
15077         
15078         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15079         
15080         
15081         
15082         
15083         
15084     },
15085     
15086     setActive: function(state)
15087     {
15088         Roo.log("panel - set active " + this.tabId + "=" + state);
15089         
15090         this.active = state;
15091         if (!state) {
15092             this.el.removeClass('active');
15093             
15094         } else  if (!this.el.hasClass('active')) {
15095             this.el.addClass('active');
15096         }
15097         
15098         this.fireEvent('changed', this, state);
15099     }
15100     
15101     
15102 });
15103  
15104
15105  
15106
15107  /*
15108  * - LGPL
15109  *
15110  * DateField
15111  * 
15112  */
15113
15114 /**
15115  * @class Roo.bootstrap.DateField
15116  * @extends Roo.bootstrap.Input
15117  * Bootstrap DateField class
15118  * @cfg {Number} weekStart default 0
15119  * @cfg {String} viewMode default empty, (months|years)
15120  * @cfg {String} minViewMode default empty, (months|years)
15121  * @cfg {Number} startDate default -Infinity
15122  * @cfg {Number} endDate default Infinity
15123  * @cfg {Boolean} todayHighlight default false
15124  * @cfg {Boolean} todayBtn default false
15125  * @cfg {Boolean} calendarWeeks default false
15126  * @cfg {Object} daysOfWeekDisabled default empty
15127  * @cfg {Boolean} singleMode default false (true | false)
15128  * 
15129  * @cfg {Boolean} keyboardNavigation default true
15130  * @cfg {String} language default en
15131  * 
15132  * @constructor
15133  * Create a new DateField
15134  * @param {Object} config The config object
15135  */
15136
15137 Roo.bootstrap.DateField = function(config){
15138     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15139      this.addEvents({
15140             /**
15141              * @event show
15142              * Fires when this field show.
15143              * @param {Roo.bootstrap.DateField} this
15144              * @param {Mixed} date The date value
15145              */
15146             show : true,
15147             /**
15148              * @event show
15149              * Fires when this field hide.
15150              * @param {Roo.bootstrap.DateField} this
15151              * @param {Mixed} date The date value
15152              */
15153             hide : true,
15154             /**
15155              * @event select
15156              * Fires when select a date.
15157              * @param {Roo.bootstrap.DateField} this
15158              * @param {Mixed} date The date value
15159              */
15160             select : true
15161         });
15162 };
15163
15164 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15165     
15166     /**
15167      * @cfg {String} format
15168      * The default date format string which can be overriden for localization support.  The format must be
15169      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15170      */
15171     format : "m/d/y",
15172     /**
15173      * @cfg {String} altFormats
15174      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15175      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15176      */
15177     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15178     
15179     weekStart : 0,
15180     
15181     viewMode : '',
15182     
15183     minViewMode : '',
15184     
15185     todayHighlight : false,
15186     
15187     todayBtn: false,
15188     
15189     language: 'en',
15190     
15191     keyboardNavigation: true,
15192     
15193     calendarWeeks: false,
15194     
15195     startDate: -Infinity,
15196     
15197     endDate: Infinity,
15198     
15199     daysOfWeekDisabled: [],
15200     
15201     _events: [],
15202     
15203     singleMode : false,
15204     
15205     UTCDate: function()
15206     {
15207         return new Date(Date.UTC.apply(Date, arguments));
15208     },
15209     
15210     UTCToday: function()
15211     {
15212         var today = new Date();
15213         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15214     },
15215     
15216     getDate: function() {
15217             var d = this.getUTCDate();
15218             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15219     },
15220     
15221     getUTCDate: function() {
15222             return this.date;
15223     },
15224     
15225     setDate: function(d) {
15226             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15227     },
15228     
15229     setUTCDate: function(d) {
15230             this.date = d;
15231             this.setValue(this.formatDate(this.date));
15232     },
15233         
15234     onRender: function(ct, position)
15235     {
15236         
15237         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15238         
15239         this.language = this.language || 'en';
15240         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15241         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15242         
15243         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15244         this.format = this.format || 'm/d/y';
15245         this.isInline = false;
15246         this.isInput = true;
15247         this.component = this.el.select('.add-on', true).first() || false;
15248         this.component = (this.component && this.component.length === 0) ? false : this.component;
15249         this.hasInput = this.component && this.inputEL().length;
15250         
15251         if (typeof(this.minViewMode === 'string')) {
15252             switch (this.minViewMode) {
15253                 case 'months':
15254                     this.minViewMode = 1;
15255                     break;
15256                 case 'years':
15257                     this.minViewMode = 2;
15258                     break;
15259                 default:
15260                     this.minViewMode = 0;
15261                     break;
15262             }
15263         }
15264         
15265         if (typeof(this.viewMode === 'string')) {
15266             switch (this.viewMode) {
15267                 case 'months':
15268                     this.viewMode = 1;
15269                     break;
15270                 case 'years':
15271                     this.viewMode = 2;
15272                     break;
15273                 default:
15274                     this.viewMode = 0;
15275                     break;
15276             }
15277         }
15278                 
15279         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15280         
15281 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15282         
15283         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15284         
15285         this.picker().on('mousedown', this.onMousedown, this);
15286         this.picker().on('click', this.onClick, this);
15287         
15288         this.picker().addClass('datepicker-dropdown');
15289         
15290         this.startViewMode = this.viewMode;
15291         
15292         if(this.singleMode){
15293             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15294                 v.setVisibilityMode(Roo.Element.DISPLAY)
15295                 v.hide();
15296             });
15297             
15298             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15299                 v.setStyle('width', '189px');
15300             });
15301         }
15302         
15303         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15304             if(!this.calendarWeeks){
15305                 v.remove();
15306                 return;
15307             }
15308             
15309             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15310             v.attr('colspan', function(i, val){
15311                 return parseInt(val) + 1;
15312             });
15313         })
15314                         
15315         
15316         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15317         
15318         this.setStartDate(this.startDate);
15319         this.setEndDate(this.endDate);
15320         
15321         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15322         
15323         this.fillDow();
15324         this.fillMonths();
15325         this.update();
15326         this.showMode();
15327         
15328         if(this.isInline) {
15329             this.show();
15330         }
15331     },
15332     
15333     picker : function()
15334     {
15335         return this.pickerEl;
15336 //        return this.el.select('.datepicker', true).first();
15337     },
15338     
15339     fillDow: function()
15340     {
15341         var dowCnt = this.weekStart;
15342         
15343         var dow = {
15344             tag: 'tr',
15345             cn: [
15346                 
15347             ]
15348         };
15349         
15350         if(this.calendarWeeks){
15351             dow.cn.push({
15352                 tag: 'th',
15353                 cls: 'cw',
15354                 html: '&nbsp;'
15355             })
15356         }
15357         
15358         while (dowCnt < this.weekStart + 7) {
15359             dow.cn.push({
15360                 tag: 'th',
15361                 cls: 'dow',
15362                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15363             });
15364         }
15365         
15366         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15367     },
15368     
15369     fillMonths: function()
15370     {    
15371         var i = 0;
15372         var months = this.picker().select('>.datepicker-months td', true).first();
15373         
15374         months.dom.innerHTML = '';
15375         
15376         while (i < 12) {
15377             var month = {
15378                 tag: 'span',
15379                 cls: 'month',
15380                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15381             }
15382             
15383             months.createChild(month);
15384         }
15385         
15386     },
15387     
15388     update: function()
15389     {
15390         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;
15391         
15392         if (this.date < this.startDate) {
15393             this.viewDate = new Date(this.startDate);
15394         } else if (this.date > this.endDate) {
15395             this.viewDate = new Date(this.endDate);
15396         } else {
15397             this.viewDate = new Date(this.date);
15398         }
15399         
15400         this.fill();
15401     },
15402     
15403     fill: function() 
15404     {
15405         var d = new Date(this.viewDate),
15406                 year = d.getUTCFullYear(),
15407                 month = d.getUTCMonth(),
15408                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15409                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15410                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15411                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15412                 currentDate = this.date && this.date.valueOf(),
15413                 today = this.UTCToday();
15414         
15415         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15416         
15417 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15418         
15419 //        this.picker.select('>tfoot th.today').
15420 //                                              .text(dates[this.language].today)
15421 //                                              .toggle(this.todayBtn !== false);
15422     
15423         this.updateNavArrows();
15424         this.fillMonths();
15425                                                 
15426         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15427         
15428         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15429          
15430         prevMonth.setUTCDate(day);
15431         
15432         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15433         
15434         var nextMonth = new Date(prevMonth);
15435         
15436         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15437         
15438         nextMonth = nextMonth.valueOf();
15439         
15440         var fillMonths = false;
15441         
15442         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15443         
15444         while(prevMonth.valueOf() < nextMonth) {
15445             var clsName = '';
15446             
15447             if (prevMonth.getUTCDay() === this.weekStart) {
15448                 if(fillMonths){
15449                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15450                 }
15451                     
15452                 fillMonths = {
15453                     tag: 'tr',
15454                     cn: []
15455                 };
15456                 
15457                 if(this.calendarWeeks){
15458                     // ISO 8601: First week contains first thursday.
15459                     // ISO also states week starts on Monday, but we can be more abstract here.
15460                     var
15461                     // Start of current week: based on weekstart/current date
15462                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15463                     // Thursday of this week
15464                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15465                     // First Thursday of year, year from thursday
15466                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15467                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15468                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15469                     
15470                     fillMonths.cn.push({
15471                         tag: 'td',
15472                         cls: 'cw',
15473                         html: calWeek
15474                     });
15475                 }
15476             }
15477             
15478             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15479                 clsName += ' old';
15480             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15481                 clsName += ' new';
15482             }
15483             if (this.todayHighlight &&
15484                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15485                 prevMonth.getUTCMonth() == today.getMonth() &&
15486                 prevMonth.getUTCDate() == today.getDate()) {
15487                 clsName += ' today';
15488             }
15489             
15490             if (currentDate && prevMonth.valueOf() === currentDate) {
15491                 clsName += ' active';
15492             }
15493             
15494             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15495                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15496                     clsName += ' disabled';
15497             }
15498             
15499             fillMonths.cn.push({
15500                 tag: 'td',
15501                 cls: 'day ' + clsName,
15502                 html: prevMonth.getDate()
15503             })
15504             
15505             prevMonth.setDate(prevMonth.getDate()+1);
15506         }
15507           
15508         var currentYear = this.date && this.date.getUTCFullYear();
15509         var currentMonth = this.date && this.date.getUTCMonth();
15510         
15511         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15512         
15513         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15514             v.removeClass('active');
15515             
15516             if(currentYear === year && k === currentMonth){
15517                 v.addClass('active');
15518             }
15519             
15520             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15521                 v.addClass('disabled');
15522             }
15523             
15524         });
15525         
15526         
15527         year = parseInt(year/10, 10) * 10;
15528         
15529         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15530         
15531         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15532         
15533         year -= 1;
15534         for (var i = -1; i < 11; i++) {
15535             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15536                 tag: 'span',
15537                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15538                 html: year
15539             })
15540             
15541             year += 1;
15542         }
15543     },
15544     
15545     showMode: function(dir) 
15546     {
15547         if (dir) {
15548             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15549         }
15550         
15551         Roo.each(this.picker().select('>div',true).elements, function(v){
15552             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15553             v.hide();
15554         });
15555         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15556     },
15557     
15558     place: function()
15559     {
15560         if(this.isInline) return;
15561         
15562         this.picker().removeClass(['bottom', 'top']);
15563         
15564         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15565             /*
15566              * place to the top of element!
15567              *
15568              */
15569             
15570             this.picker().addClass('top');
15571             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15572             
15573             return;
15574         }
15575         
15576         this.picker().addClass('bottom');
15577         
15578         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15579     },
15580     
15581     parseDate : function(value)
15582     {
15583         if(!value || value instanceof Date){
15584             return value;
15585         }
15586         var v = Date.parseDate(value, this.format);
15587         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15588             v = Date.parseDate(value, 'Y-m-d');
15589         }
15590         if(!v && this.altFormats){
15591             if(!this.altFormatsArray){
15592                 this.altFormatsArray = this.altFormats.split("|");
15593             }
15594             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15595                 v = Date.parseDate(value, this.altFormatsArray[i]);
15596             }
15597         }
15598         return v;
15599     },
15600     
15601     formatDate : function(date, fmt)
15602     {   
15603         return (!date || !(date instanceof Date)) ?
15604         date : date.dateFormat(fmt || this.format);
15605     },
15606     
15607     onFocus : function()
15608     {
15609         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15610         this.show();
15611     },
15612     
15613     onBlur : function()
15614     {
15615         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15616         
15617         var d = this.inputEl().getValue();
15618         
15619         this.setValue(d);
15620                 
15621         this.hide();
15622     },
15623     
15624     show : function()
15625     {
15626         this.picker().show();
15627         this.update();
15628         this.place();
15629         
15630         this.fireEvent('show', this, this.date);
15631     },
15632     
15633     hide : function()
15634     {
15635         if(this.isInline) return;
15636         this.picker().hide();
15637         this.viewMode = this.startViewMode;
15638         this.showMode();
15639         
15640         this.fireEvent('hide', this, this.date);
15641         
15642     },
15643     
15644     onMousedown: function(e)
15645     {
15646         e.stopPropagation();
15647         e.preventDefault();
15648     },
15649     
15650     keyup: function(e)
15651     {
15652         Roo.bootstrap.DateField.superclass.keyup.call(this);
15653         this.update();
15654     },
15655
15656     setValue: function(v)
15657     {
15658         
15659         // v can be a string or a date..
15660         
15661         
15662         var d = new Date(this.parseDate(v) ).clearTime();
15663         
15664         if(isNaN(d.getTime())){
15665             this.date = this.viewDate = '';
15666             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15667             return;
15668         }
15669         
15670         v = this.formatDate(d);
15671         
15672         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15673         
15674         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15675      
15676         this.update();
15677
15678         this.fireEvent('select', this, this.date);
15679         
15680     },
15681     
15682     getValue: function()
15683     {
15684         return this.formatDate(this.date);
15685     },
15686     
15687     fireKey: function(e)
15688     {
15689         if (!this.picker().isVisible()){
15690             if (e.keyCode == 27) // allow escape to hide and re-show picker
15691                 this.show();
15692             return;
15693         }
15694         
15695         var dateChanged = false,
15696         dir, day, month,
15697         newDate, newViewDate;
15698         
15699         switch(e.keyCode){
15700             case 27: // escape
15701                 this.hide();
15702                 e.preventDefault();
15703                 break;
15704             case 37: // left
15705             case 39: // right
15706                 if (!this.keyboardNavigation) break;
15707                 dir = e.keyCode == 37 ? -1 : 1;
15708                 
15709                 if (e.ctrlKey){
15710                     newDate = this.moveYear(this.date, dir);
15711                     newViewDate = this.moveYear(this.viewDate, dir);
15712                 } else if (e.shiftKey){
15713                     newDate = this.moveMonth(this.date, dir);
15714                     newViewDate = this.moveMonth(this.viewDate, dir);
15715                 } else {
15716                     newDate = new Date(this.date);
15717                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15718                     newViewDate = new Date(this.viewDate);
15719                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15720                 }
15721                 if (this.dateWithinRange(newDate)){
15722                     this.date = newDate;
15723                     this.viewDate = newViewDate;
15724                     this.setValue(this.formatDate(this.date));
15725 //                    this.update();
15726                     e.preventDefault();
15727                     dateChanged = true;
15728                 }
15729                 break;
15730             case 38: // up
15731             case 40: // down
15732                 if (!this.keyboardNavigation) break;
15733                 dir = e.keyCode == 38 ? -1 : 1;
15734                 if (e.ctrlKey){
15735                     newDate = this.moveYear(this.date, dir);
15736                     newViewDate = this.moveYear(this.viewDate, dir);
15737                 } else if (e.shiftKey){
15738                     newDate = this.moveMonth(this.date, dir);
15739                     newViewDate = this.moveMonth(this.viewDate, dir);
15740                 } else {
15741                     newDate = new Date(this.date);
15742                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15743                     newViewDate = new Date(this.viewDate);
15744                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15745                 }
15746                 if (this.dateWithinRange(newDate)){
15747                     this.date = newDate;
15748                     this.viewDate = newViewDate;
15749                     this.setValue(this.formatDate(this.date));
15750 //                    this.update();
15751                     e.preventDefault();
15752                     dateChanged = true;
15753                 }
15754                 break;
15755             case 13: // enter
15756                 this.setValue(this.formatDate(this.date));
15757                 this.hide();
15758                 e.preventDefault();
15759                 break;
15760             case 9: // tab
15761                 this.setValue(this.formatDate(this.date));
15762                 this.hide();
15763                 break;
15764             case 16: // shift
15765             case 17: // ctrl
15766             case 18: // alt
15767                 break;
15768             default :
15769                 this.hide();
15770                 
15771         }
15772     },
15773     
15774     
15775     onClick: function(e) 
15776     {
15777         e.stopPropagation();
15778         e.preventDefault();
15779         
15780         var target = e.getTarget();
15781         
15782         if(target.nodeName.toLowerCase() === 'i'){
15783             target = Roo.get(target).dom.parentNode;
15784         }
15785         
15786         var nodeName = target.nodeName;
15787         var className = target.className;
15788         var html = target.innerHTML;
15789         //Roo.log(nodeName);
15790         
15791         switch(nodeName.toLowerCase()) {
15792             case 'th':
15793                 switch(className) {
15794                     case 'switch':
15795                         this.showMode(1);
15796                         break;
15797                     case 'prev':
15798                     case 'next':
15799                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15800                         switch(this.viewMode){
15801                                 case 0:
15802                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15803                                         break;
15804                                 case 1:
15805                                 case 2:
15806                                         this.viewDate = this.moveYear(this.viewDate, dir);
15807                                         break;
15808                         }
15809                         this.fill();
15810                         break;
15811                     case 'today':
15812                         var date = new Date();
15813                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15814 //                        this.fill()
15815                         this.setValue(this.formatDate(this.date));
15816                         
15817                         this.hide();
15818                         break;
15819                 }
15820                 break;
15821             case 'span':
15822                 if (className.indexOf('disabled') < 0) {
15823                     this.viewDate.setUTCDate(1);
15824                     if (className.indexOf('month') > -1) {
15825                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15826                     } else {
15827                         var year = parseInt(html, 10) || 0;
15828                         this.viewDate.setUTCFullYear(year);
15829                         
15830                     }
15831                     
15832                     if(this.singleMode){
15833                         this.setValue(this.formatDate(this.viewDate));
15834                         this.hide();
15835                         return;
15836                     }
15837                     
15838                     this.showMode(-1);
15839                     this.fill();
15840                 }
15841                 break;
15842                 
15843             case 'td':
15844                 //Roo.log(className);
15845                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15846                     var day = parseInt(html, 10) || 1;
15847                     var year = this.viewDate.getUTCFullYear(),
15848                         month = this.viewDate.getUTCMonth();
15849
15850                     if (className.indexOf('old') > -1) {
15851                         if(month === 0 ){
15852                             month = 11;
15853                             year -= 1;
15854                         }else{
15855                             month -= 1;
15856                         }
15857                     } else if (className.indexOf('new') > -1) {
15858                         if (month == 11) {
15859                             month = 0;
15860                             year += 1;
15861                         } else {
15862                             month += 1;
15863                         }
15864                     }
15865                     //Roo.log([year,month,day]);
15866                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15867                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15868 //                    this.fill();
15869                     //Roo.log(this.formatDate(this.date));
15870                     this.setValue(this.formatDate(this.date));
15871                     this.hide();
15872                 }
15873                 break;
15874         }
15875     },
15876     
15877     setStartDate: function(startDate)
15878     {
15879         this.startDate = startDate || -Infinity;
15880         if (this.startDate !== -Infinity) {
15881             this.startDate = this.parseDate(this.startDate);
15882         }
15883         this.update();
15884         this.updateNavArrows();
15885     },
15886
15887     setEndDate: function(endDate)
15888     {
15889         this.endDate = endDate || Infinity;
15890         if (this.endDate !== Infinity) {
15891             this.endDate = this.parseDate(this.endDate);
15892         }
15893         this.update();
15894         this.updateNavArrows();
15895     },
15896     
15897     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15898     {
15899         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15900         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15901             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15902         }
15903         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15904             return parseInt(d, 10);
15905         });
15906         this.update();
15907         this.updateNavArrows();
15908     },
15909     
15910     updateNavArrows: function() 
15911     {
15912         if(this.singleMode){
15913             return;
15914         }
15915         
15916         var d = new Date(this.viewDate),
15917         year = d.getUTCFullYear(),
15918         month = d.getUTCMonth();
15919         
15920         Roo.each(this.picker().select('.prev', true).elements, function(v){
15921             v.show();
15922             switch (this.viewMode) {
15923                 case 0:
15924
15925                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15926                         v.hide();
15927                     }
15928                     break;
15929                 case 1:
15930                 case 2:
15931                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15932                         v.hide();
15933                     }
15934                     break;
15935             }
15936         });
15937         
15938         Roo.each(this.picker().select('.next', true).elements, function(v){
15939             v.show();
15940             switch (this.viewMode) {
15941                 case 0:
15942
15943                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15944                         v.hide();
15945                     }
15946                     break;
15947                 case 1:
15948                 case 2:
15949                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15950                         v.hide();
15951                     }
15952                     break;
15953             }
15954         })
15955     },
15956     
15957     moveMonth: function(date, dir)
15958     {
15959         if (!dir) return date;
15960         var new_date = new Date(date.valueOf()),
15961         day = new_date.getUTCDate(),
15962         month = new_date.getUTCMonth(),
15963         mag = Math.abs(dir),
15964         new_month, test;
15965         dir = dir > 0 ? 1 : -1;
15966         if (mag == 1){
15967             test = dir == -1
15968             // If going back one month, make sure month is not current month
15969             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15970             ? function(){
15971                 return new_date.getUTCMonth() == month;
15972             }
15973             // If going forward one month, make sure month is as expected
15974             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15975             : function(){
15976                 return new_date.getUTCMonth() != new_month;
15977             };
15978             new_month = month + dir;
15979             new_date.setUTCMonth(new_month);
15980             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15981             if (new_month < 0 || new_month > 11)
15982                 new_month = (new_month + 12) % 12;
15983         } else {
15984             // For magnitudes >1, move one month at a time...
15985             for (var i=0; i<mag; i++)
15986                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15987                 new_date = this.moveMonth(new_date, dir);
15988             // ...then reset the day, keeping it in the new month
15989             new_month = new_date.getUTCMonth();
15990             new_date.setUTCDate(day);
15991             test = function(){
15992                 return new_month != new_date.getUTCMonth();
15993             };
15994         }
15995         // Common date-resetting loop -- if date is beyond end of month, make it
15996         // end of month
15997         while (test()){
15998             new_date.setUTCDate(--day);
15999             new_date.setUTCMonth(new_month);
16000         }
16001         return new_date;
16002     },
16003
16004     moveYear: function(date, dir)
16005     {
16006         return this.moveMonth(date, dir*12);
16007     },
16008
16009     dateWithinRange: function(date)
16010     {
16011         return date >= this.startDate && date <= this.endDate;
16012     },
16013
16014     
16015     remove: function() 
16016     {
16017         this.picker().remove();
16018     }
16019    
16020 });
16021
16022 Roo.apply(Roo.bootstrap.DateField,  {
16023     
16024     head : {
16025         tag: 'thead',
16026         cn: [
16027         {
16028             tag: 'tr',
16029             cn: [
16030             {
16031                 tag: 'th',
16032                 cls: 'prev',
16033                 html: '<i class="fa fa-arrow-left"/>'
16034             },
16035             {
16036                 tag: 'th',
16037                 cls: 'switch',
16038                 colspan: '5'
16039             },
16040             {
16041                 tag: 'th',
16042                 cls: 'next',
16043                 html: '<i class="fa fa-arrow-right"/>'
16044             }
16045
16046             ]
16047         }
16048         ]
16049     },
16050     
16051     content : {
16052         tag: 'tbody',
16053         cn: [
16054         {
16055             tag: 'tr',
16056             cn: [
16057             {
16058                 tag: 'td',
16059                 colspan: '7'
16060             }
16061             ]
16062         }
16063         ]
16064     },
16065     
16066     footer : {
16067         tag: 'tfoot',
16068         cn: [
16069         {
16070             tag: 'tr',
16071             cn: [
16072             {
16073                 tag: 'th',
16074                 colspan: '7',
16075                 cls: 'today'
16076             }
16077                     
16078             ]
16079         }
16080         ]
16081     },
16082     
16083     dates:{
16084         en: {
16085             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16086             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16087             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16088             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16089             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16090             today: "Today"
16091         }
16092     },
16093     
16094     modes: [
16095     {
16096         clsName: 'days',
16097         navFnc: 'Month',
16098         navStep: 1
16099     },
16100     {
16101         clsName: 'months',
16102         navFnc: 'FullYear',
16103         navStep: 1
16104     },
16105     {
16106         clsName: 'years',
16107         navFnc: 'FullYear',
16108         navStep: 10
16109     }]
16110 });
16111
16112 Roo.apply(Roo.bootstrap.DateField,  {
16113   
16114     template : {
16115         tag: 'div',
16116         cls: 'datepicker dropdown-menu roo-dynamic',
16117         cn: [
16118         {
16119             tag: 'div',
16120             cls: 'datepicker-days',
16121             cn: [
16122             {
16123                 tag: 'table',
16124                 cls: 'table-condensed',
16125                 cn:[
16126                 Roo.bootstrap.DateField.head,
16127                 {
16128                     tag: 'tbody'
16129                 },
16130                 Roo.bootstrap.DateField.footer
16131                 ]
16132             }
16133             ]
16134         },
16135         {
16136             tag: 'div',
16137             cls: 'datepicker-months',
16138             cn: [
16139             {
16140                 tag: 'table',
16141                 cls: 'table-condensed',
16142                 cn:[
16143                 Roo.bootstrap.DateField.head,
16144                 Roo.bootstrap.DateField.content,
16145                 Roo.bootstrap.DateField.footer
16146                 ]
16147             }
16148             ]
16149         },
16150         {
16151             tag: 'div',
16152             cls: 'datepicker-years',
16153             cn: [
16154             {
16155                 tag: 'table',
16156                 cls: 'table-condensed',
16157                 cn:[
16158                 Roo.bootstrap.DateField.head,
16159                 Roo.bootstrap.DateField.content,
16160                 Roo.bootstrap.DateField.footer
16161                 ]
16162             }
16163             ]
16164         }
16165         ]
16166     }
16167 });
16168
16169  
16170
16171  /*
16172  * - LGPL
16173  *
16174  * TimeField
16175  * 
16176  */
16177
16178 /**
16179  * @class Roo.bootstrap.TimeField
16180  * @extends Roo.bootstrap.Input
16181  * Bootstrap DateField class
16182  * 
16183  * 
16184  * @constructor
16185  * Create a new TimeField
16186  * @param {Object} config The config object
16187  */
16188
16189 Roo.bootstrap.TimeField = function(config){
16190     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16191     this.addEvents({
16192             /**
16193              * @event show
16194              * Fires when this field show.
16195              * @param {Roo.bootstrap.DateField} thisthis
16196              * @param {Mixed} date The date value
16197              */
16198             show : true,
16199             /**
16200              * @event show
16201              * Fires when this field hide.
16202              * @param {Roo.bootstrap.DateField} this
16203              * @param {Mixed} date The date value
16204              */
16205             hide : true,
16206             /**
16207              * @event select
16208              * Fires when select a date.
16209              * @param {Roo.bootstrap.DateField} this
16210              * @param {Mixed} date The date value
16211              */
16212             select : true
16213         });
16214 };
16215
16216 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16217     
16218     /**
16219      * @cfg {String} format
16220      * The default time format string which can be overriden for localization support.  The format must be
16221      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16222      */
16223     format : "H:i",
16224        
16225     onRender: function(ct, position)
16226     {
16227         
16228         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16229                 
16230         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16231         
16232         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16233         
16234         this.pop = this.picker().select('>.datepicker-time',true).first();
16235         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16236         
16237         this.picker().on('mousedown', this.onMousedown, this);
16238         this.picker().on('click', this.onClick, this);
16239         
16240         this.picker().addClass('datepicker-dropdown');
16241     
16242         this.fillTime();
16243         this.update();
16244             
16245         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16246         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16247         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16248         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16249         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16250         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16251
16252     },
16253     
16254     fireKey: function(e){
16255         if (!this.picker().isVisible()){
16256             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16257                 this.show();
16258             }
16259             return;
16260         }
16261
16262         e.preventDefault();
16263         
16264         switch(e.keyCode){
16265             case 27: // escape
16266                 this.hide();
16267                 break;
16268             case 37: // left
16269             case 39: // right
16270                 this.onTogglePeriod();
16271                 break;
16272             case 38: // up
16273                 this.onIncrementMinutes();
16274                 break;
16275             case 40: // down
16276                 this.onDecrementMinutes();
16277                 break;
16278             case 13: // enter
16279             case 9: // tab
16280                 this.setTime();
16281                 break;
16282         }
16283     },
16284     
16285     onClick: function(e) {
16286         e.stopPropagation();
16287         e.preventDefault();
16288     },
16289     
16290     picker : function()
16291     {
16292         return this.el.select('.datepicker', true).first();
16293     },
16294     
16295     fillTime: function()
16296     {    
16297         var time = this.pop.select('tbody', true).first();
16298         
16299         time.dom.innerHTML = '';
16300         
16301         time.createChild({
16302             tag: 'tr',
16303             cn: [
16304                 {
16305                     tag: 'td',
16306                     cn: [
16307                         {
16308                             tag: 'a',
16309                             href: '#',
16310                             cls: 'btn',
16311                             cn: [
16312                                 {
16313                                     tag: 'span',
16314                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16315                                 }
16316                             ]
16317                         } 
16318                     ]
16319                 },
16320                 {
16321                     tag: 'td',
16322                     cls: 'separator'
16323                 },
16324                 {
16325                     tag: 'td',
16326                     cn: [
16327                         {
16328                             tag: 'a',
16329                             href: '#',
16330                             cls: 'btn',
16331                             cn: [
16332                                 {
16333                                     tag: 'span',
16334                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16335                                 }
16336                             ]
16337                         }
16338                     ]
16339                 },
16340                 {
16341                     tag: 'td',
16342                     cls: 'separator'
16343                 }
16344             ]
16345         });
16346         
16347         time.createChild({
16348             tag: 'tr',
16349             cn: [
16350                 {
16351                     tag: 'td',
16352                     cn: [
16353                         {
16354                             tag: 'span',
16355                             cls: 'timepicker-hour',
16356                             html: '00'
16357                         }  
16358                     ]
16359                 },
16360                 {
16361                     tag: 'td',
16362                     cls: 'separator',
16363                     html: ':'
16364                 },
16365                 {
16366                     tag: 'td',
16367                     cn: [
16368                         {
16369                             tag: 'span',
16370                             cls: 'timepicker-minute',
16371                             html: '00'
16372                         }  
16373                     ]
16374                 },
16375                 {
16376                     tag: 'td',
16377                     cls: 'separator'
16378                 },
16379                 {
16380                     tag: 'td',
16381                     cn: [
16382                         {
16383                             tag: 'button',
16384                             type: 'button',
16385                             cls: 'btn btn-primary period',
16386                             html: 'AM'
16387                             
16388                         }
16389                     ]
16390                 }
16391             ]
16392         });
16393         
16394         time.createChild({
16395             tag: 'tr',
16396             cn: [
16397                 {
16398                     tag: 'td',
16399                     cn: [
16400                         {
16401                             tag: 'a',
16402                             href: '#',
16403                             cls: 'btn',
16404                             cn: [
16405                                 {
16406                                     tag: 'span',
16407                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16408                                 }
16409                             ]
16410                         }
16411                     ]
16412                 },
16413                 {
16414                     tag: 'td',
16415                     cls: 'separator'
16416                 },
16417                 {
16418                     tag: 'td',
16419                     cn: [
16420                         {
16421                             tag: 'a',
16422                             href: '#',
16423                             cls: 'btn',
16424                             cn: [
16425                                 {
16426                                     tag: 'span',
16427                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16428                                 }
16429                             ]
16430                         }
16431                     ]
16432                 },
16433                 {
16434                     tag: 'td',
16435                     cls: 'separator'
16436                 }
16437             ]
16438         });
16439         
16440     },
16441     
16442     update: function()
16443     {
16444         
16445         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16446         
16447         this.fill();
16448     },
16449     
16450     fill: function() 
16451     {
16452         var hours = this.time.getHours();
16453         var minutes = this.time.getMinutes();
16454         var period = 'AM';
16455         
16456         if(hours > 11){
16457             period = 'PM';
16458         }
16459         
16460         if(hours == 0){
16461             hours = 12;
16462         }
16463         
16464         
16465         if(hours > 12){
16466             hours = hours - 12;
16467         }
16468         
16469         if(hours < 10){
16470             hours = '0' + hours;
16471         }
16472         
16473         if(minutes < 10){
16474             minutes = '0' + minutes;
16475         }
16476         
16477         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16478         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16479         this.pop.select('button', true).first().dom.innerHTML = period;
16480         
16481     },
16482     
16483     place: function()
16484     {   
16485         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16486         
16487         var cls = ['bottom'];
16488         
16489         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16490             cls.pop();
16491             cls.push('top');
16492         }
16493         
16494         cls.push('right');
16495         
16496         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16497             cls.pop();
16498             cls.push('left');
16499         }
16500         
16501         this.picker().addClass(cls.join('-'));
16502         
16503         var _this = this;
16504         
16505         Roo.each(cls, function(c){
16506             if(c == 'bottom'){
16507                 _this.picker().setTop(_this.inputEl().getHeight());
16508                 return;
16509             }
16510             if(c == 'top'){
16511                 _this.picker().setTop(0 - _this.picker().getHeight());
16512                 return;
16513             }
16514             
16515             if(c == 'left'){
16516                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16517                 return;
16518             }
16519             if(c == 'right'){
16520                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16521                 return;
16522             }
16523         });
16524         
16525     },
16526   
16527     onFocus : function()
16528     {
16529         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16530         this.show();
16531     },
16532     
16533     onBlur : function()
16534     {
16535         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16536         this.hide();
16537     },
16538     
16539     show : function()
16540     {
16541         this.picker().show();
16542         this.pop.show();
16543         this.update();
16544         this.place();
16545         
16546         this.fireEvent('show', this, this.date);
16547     },
16548     
16549     hide : function()
16550     {
16551         this.picker().hide();
16552         this.pop.hide();
16553         
16554         this.fireEvent('hide', this, this.date);
16555     },
16556     
16557     setTime : function()
16558     {
16559         this.hide();
16560         this.setValue(this.time.format(this.format));
16561         
16562         this.fireEvent('select', this, this.date);
16563         
16564         
16565     },
16566     
16567     onMousedown: function(e){
16568         e.stopPropagation();
16569         e.preventDefault();
16570     },
16571     
16572     onIncrementHours: function()
16573     {
16574         Roo.log('onIncrementHours');
16575         this.time = this.time.add(Date.HOUR, 1);
16576         this.update();
16577         
16578     },
16579     
16580     onDecrementHours: function()
16581     {
16582         Roo.log('onDecrementHours');
16583         this.time = this.time.add(Date.HOUR, -1);
16584         this.update();
16585     },
16586     
16587     onIncrementMinutes: function()
16588     {
16589         Roo.log('onIncrementMinutes');
16590         this.time = this.time.add(Date.MINUTE, 1);
16591         this.update();
16592     },
16593     
16594     onDecrementMinutes: function()
16595     {
16596         Roo.log('onDecrementMinutes');
16597         this.time = this.time.add(Date.MINUTE, -1);
16598         this.update();
16599     },
16600     
16601     onTogglePeriod: function()
16602     {
16603         Roo.log('onTogglePeriod');
16604         this.time = this.time.add(Date.HOUR, 12);
16605         this.update();
16606     }
16607     
16608    
16609 });
16610
16611 Roo.apply(Roo.bootstrap.TimeField,  {
16612     
16613     content : {
16614         tag: 'tbody',
16615         cn: [
16616             {
16617                 tag: 'tr',
16618                 cn: [
16619                 {
16620                     tag: 'td',
16621                     colspan: '7'
16622                 }
16623                 ]
16624             }
16625         ]
16626     },
16627     
16628     footer : {
16629         tag: 'tfoot',
16630         cn: [
16631             {
16632                 tag: 'tr',
16633                 cn: [
16634                 {
16635                     tag: 'th',
16636                     colspan: '7',
16637                     cls: '',
16638                     cn: [
16639                         {
16640                             tag: 'button',
16641                             cls: 'btn btn-info ok',
16642                             html: 'OK'
16643                         }
16644                     ]
16645                 }
16646
16647                 ]
16648             }
16649         ]
16650     }
16651 });
16652
16653 Roo.apply(Roo.bootstrap.TimeField,  {
16654   
16655     template : {
16656         tag: 'div',
16657         cls: 'datepicker dropdown-menu',
16658         cn: [
16659             {
16660                 tag: 'div',
16661                 cls: 'datepicker-time',
16662                 cn: [
16663                 {
16664                     tag: 'table',
16665                     cls: 'table-condensed',
16666                     cn:[
16667                     Roo.bootstrap.TimeField.content,
16668                     Roo.bootstrap.TimeField.footer
16669                     ]
16670                 }
16671                 ]
16672             }
16673         ]
16674     }
16675 });
16676
16677  
16678
16679  /*
16680  * - LGPL
16681  *
16682  * MonthField
16683  * 
16684  */
16685
16686 /**
16687  * @class Roo.bootstrap.MonthField
16688  * @extends Roo.bootstrap.Input
16689  * Bootstrap MonthField class
16690  * 
16691  * @cfg {String} language default en
16692  * 
16693  * @constructor
16694  * Create a new MonthField
16695  * @param {Object} config The config object
16696  */
16697
16698 Roo.bootstrap.MonthField = function(config){
16699     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16700     
16701     this.addEvents({
16702         /**
16703          * @event show
16704          * Fires when this field show.
16705          * @param {Roo.bootstrap.MonthField} this
16706          * @param {Mixed} date The date value
16707          */
16708         show : true,
16709         /**
16710          * @event show
16711          * Fires when this field hide.
16712          * @param {Roo.bootstrap.MonthField} this
16713          * @param {Mixed} date The date value
16714          */
16715         hide : true,
16716         /**
16717          * @event select
16718          * Fires when select a date.
16719          * @param {Roo.bootstrap.MonthField} this
16720          * @param {String} oldvalue The old value
16721          * @param {String} newvalue The new value
16722          */
16723         select : true
16724     });
16725 };
16726
16727 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16728     
16729     onRender: function(ct, position)
16730     {
16731         
16732         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16733         
16734         this.language = this.language || 'en';
16735         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16736         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16737         
16738         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16739         this.isInline = false;
16740         this.isInput = true;
16741         this.component = this.el.select('.add-on', true).first() || false;
16742         this.component = (this.component && this.component.length === 0) ? false : this.component;
16743         this.hasInput = this.component && this.inputEL().length;
16744         
16745         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16746         
16747         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16748         
16749         this.picker().on('mousedown', this.onMousedown, this);
16750         this.picker().on('click', this.onClick, this);
16751         
16752         this.picker().addClass('datepicker-dropdown');
16753         
16754         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16755             v.setStyle('width', '189px');
16756         });
16757         
16758         this.fillMonths();
16759         
16760         this.update();
16761         
16762         if(this.isInline) {
16763             this.show();
16764         }
16765         
16766     },
16767     
16768     setValue: function(v, suppressEvent)
16769     {   
16770         var o = this.getValue();
16771         
16772         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16773         
16774         this.update();
16775
16776         if(suppressEvent !== true){
16777             this.fireEvent('select', this, o, v);
16778         }
16779         
16780     },
16781     
16782     getValue: function()
16783     {
16784         return this.value;
16785     },
16786     
16787     onClick: function(e) 
16788     {
16789         e.stopPropagation();
16790         e.preventDefault();
16791         
16792         var target = e.getTarget();
16793         
16794         if(target.nodeName.toLowerCase() === 'i'){
16795             target = Roo.get(target).dom.parentNode;
16796         }
16797         
16798         var nodeName = target.nodeName;
16799         var className = target.className;
16800         var html = target.innerHTML;
16801         
16802         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16803             return;
16804         }
16805         
16806         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16807         
16808         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16809         
16810         this.hide();
16811                         
16812     },
16813     
16814     picker : function()
16815     {
16816         return this.pickerEl;
16817     },
16818     
16819     fillMonths: function()
16820     {    
16821         var i = 0;
16822         var months = this.picker().select('>.datepicker-months td', true).first();
16823         
16824         months.dom.innerHTML = '';
16825         
16826         while (i < 12) {
16827             var month = {
16828                 tag: 'span',
16829                 cls: 'month',
16830                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16831             }
16832             
16833             months.createChild(month);
16834         }
16835         
16836     },
16837     
16838     update: function()
16839     {
16840         var _this = this;
16841         
16842         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16843             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16844         }
16845         
16846         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16847             e.removeClass('active');
16848             
16849             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16850                 e.addClass('active');
16851             }
16852         })
16853     },
16854     
16855     place: function()
16856     {
16857         if(this.isInline) return;
16858         
16859         this.picker().removeClass(['bottom', 'top']);
16860         
16861         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16862             /*
16863              * place to the top of element!
16864              *
16865              */
16866             
16867             this.picker().addClass('top');
16868             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16869             
16870             return;
16871         }
16872         
16873         this.picker().addClass('bottom');
16874         
16875         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16876     },
16877     
16878     onFocus : function()
16879     {
16880         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16881         this.show();
16882     },
16883     
16884     onBlur : function()
16885     {
16886         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16887         
16888         var d = this.inputEl().getValue();
16889         
16890         this.setValue(d);
16891                 
16892         this.hide();
16893     },
16894     
16895     show : function()
16896     {
16897         this.picker().show();
16898         this.picker().select('>.datepicker-months', true).first().show();
16899         this.update();
16900         this.place();
16901         
16902         this.fireEvent('show', this, this.date);
16903     },
16904     
16905     hide : function()
16906     {
16907         if(this.isInline) return;
16908         this.picker().hide();
16909         this.fireEvent('hide', this, this.date);
16910         
16911     },
16912     
16913     onMousedown: function(e)
16914     {
16915         e.stopPropagation();
16916         e.preventDefault();
16917     },
16918     
16919     keyup: function(e)
16920     {
16921         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16922         this.update();
16923     },
16924
16925     fireKey: function(e)
16926     {
16927         if (!this.picker().isVisible()){
16928             if (e.keyCode == 27) // allow escape to hide and re-show picker
16929                 this.show();
16930             return;
16931         }
16932         
16933         var dir;
16934         
16935         switch(e.keyCode){
16936             case 27: // escape
16937                 this.hide();
16938                 e.preventDefault();
16939                 break;
16940             case 37: // left
16941             case 39: // right
16942                 dir = e.keyCode == 37 ? -1 : 1;
16943                 
16944                 this.vIndex = this.vIndex + dir;
16945                 
16946                 if(this.vIndex < 0){
16947                     this.vIndex = 0;
16948                 }
16949                 
16950                 if(this.vIndex > 11){
16951                     this.vIndex = 11;
16952                 }
16953                 
16954                 if(isNaN(this.vIndex)){
16955                     this.vIndex = 0;
16956                 }
16957                 
16958                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16959                 
16960                 break;
16961             case 38: // up
16962             case 40: // down
16963                 
16964                 dir = e.keyCode == 38 ? -1 : 1;
16965                 
16966                 this.vIndex = this.vIndex + dir * 4;
16967                 
16968                 if(this.vIndex < 0){
16969                     this.vIndex = 0;
16970                 }
16971                 
16972                 if(this.vIndex > 11){
16973                     this.vIndex = 11;
16974                 }
16975                 
16976                 if(isNaN(this.vIndex)){
16977                     this.vIndex = 0;
16978                 }
16979                 
16980                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16981                 break;
16982                 
16983             case 13: // enter
16984                 
16985                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16986                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16987                 }
16988                 
16989                 this.hide();
16990                 e.preventDefault();
16991                 break;
16992             case 9: // tab
16993                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16994                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16995                 }
16996                 this.hide();
16997                 break;
16998             case 16: // shift
16999             case 17: // ctrl
17000             case 18: // alt
17001                 break;
17002             default :
17003                 this.hide();
17004                 
17005         }
17006     },
17007     
17008     remove: function() 
17009     {
17010         this.picker().remove();
17011     }
17012    
17013 });
17014
17015 Roo.apply(Roo.bootstrap.MonthField,  {
17016     
17017     content : {
17018         tag: 'tbody',
17019         cn: [
17020         {
17021             tag: 'tr',
17022             cn: [
17023             {
17024                 tag: 'td',
17025                 colspan: '7'
17026             }
17027             ]
17028         }
17029         ]
17030     },
17031     
17032     dates:{
17033         en: {
17034             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17035             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17036         }
17037     }
17038 });
17039
17040 Roo.apply(Roo.bootstrap.MonthField,  {
17041   
17042     template : {
17043         tag: 'div',
17044         cls: 'datepicker dropdown-menu roo-dynamic',
17045         cn: [
17046             {
17047                 tag: 'div',
17048                 cls: 'datepicker-months',
17049                 cn: [
17050                 {
17051                     tag: 'table',
17052                     cls: 'table-condensed',
17053                     cn:[
17054                         Roo.bootstrap.DateField.content
17055                     ]
17056                 }
17057                 ]
17058             }
17059         ]
17060     }
17061 });
17062
17063  
17064
17065  
17066  /*
17067  * - LGPL
17068  *
17069  * CheckBox
17070  * 
17071  */
17072
17073 /**
17074  * @class Roo.bootstrap.CheckBox
17075  * @extends Roo.bootstrap.Input
17076  * Bootstrap CheckBox class
17077  * 
17078  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17079  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17080  * @cfg {String} boxLabel The text that appears beside the checkbox
17081  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17082  * @cfg {Boolean} checked initnal the element
17083  * @cfg {Boolean} inline inline the element (default false)
17084  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17085  * 
17086  * @constructor
17087  * Create a new CheckBox
17088  * @param {Object} config The config object
17089  */
17090
17091 Roo.bootstrap.CheckBox = function(config){
17092     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17093    
17094     this.addEvents({
17095         /**
17096         * @event check
17097         * Fires when the element is checked or unchecked.
17098         * @param {Roo.bootstrap.CheckBox} this This input
17099         * @param {Boolean} checked The new checked value
17100         */
17101        check : true
17102     });
17103     
17104 };
17105
17106 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17107   
17108     inputType: 'checkbox',
17109     inputValue: 1,
17110     valueOff: 0,
17111     boxLabel: false,
17112     checked: false,
17113     weight : false,
17114     inline: false,
17115     
17116     getAutoCreate : function()
17117     {
17118         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17119         
17120         var id = Roo.id();
17121         
17122         var cfg = {};
17123         
17124         cfg.cls = 'form-group ' + this.inputType; //input-group
17125         
17126         if(this.inline){
17127             cfg.cls += ' ' + this.inputType + '-inline';
17128         }
17129         
17130         var input =  {
17131             tag: 'input',
17132             id : id,
17133             type : this.inputType,
17134             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17135             cls : 'roo-' + this.inputType, //'form-box',
17136             placeholder : this.placeholder || ''
17137             
17138         };
17139         
17140         if (this.weight) { // Validity check?
17141             cfg.cls += " " + this.inputType + "-" + this.weight;
17142         }
17143         
17144         if (this.disabled) {
17145             input.disabled=true;
17146         }
17147         
17148         if(this.checked){
17149             input.checked = this.checked;
17150         }
17151         
17152         if (this.name) {
17153             input.name = this.name;
17154         }
17155         
17156         if (this.size) {
17157             input.cls += ' input-' + this.size;
17158         }
17159         
17160         var settings=this;
17161         
17162         ['xs','sm','md','lg'].map(function(size){
17163             if (settings[size]) {
17164                 cfg.cls += ' col-' + size + '-' + settings[size];
17165             }
17166         });
17167         
17168         var inputblock = input;
17169          
17170         if (this.before || this.after) {
17171             
17172             inputblock = {
17173                 cls : 'input-group',
17174                 cn :  [] 
17175             };
17176             
17177             if (this.before) {
17178                 inputblock.cn.push({
17179                     tag :'span',
17180                     cls : 'input-group-addon',
17181                     html : this.before
17182                 });
17183             }
17184             
17185             inputblock.cn.push(input);
17186             
17187             if (this.after) {
17188                 inputblock.cn.push({
17189                     tag :'span',
17190                     cls : 'input-group-addon',
17191                     html : this.after
17192                 });
17193             }
17194             
17195         }
17196         
17197         if (align ==='left' && this.fieldLabel.length) {
17198                 Roo.log("left and has label");
17199                 cfg.cn = [
17200                     
17201                     {
17202                         tag: 'label',
17203                         'for' :  id,
17204                         cls : 'control-label col-md-' + this.labelWidth,
17205                         html : this.fieldLabel
17206                         
17207                     },
17208                     {
17209                         cls : "col-md-" + (12 - this.labelWidth), 
17210                         cn: [
17211                             inputblock
17212                         ]
17213                     }
17214                     
17215                 ];
17216         } else if ( this.fieldLabel.length) {
17217                 Roo.log(" label");
17218                 cfg.cn = [
17219                    
17220                     {
17221                         tag: this.boxLabel ? 'span' : 'label',
17222                         'for': id,
17223                         cls: 'control-label box-input-label',
17224                         //cls : 'input-group-addon',
17225                         html : this.fieldLabel
17226                         
17227                     },
17228                     
17229                     inputblock
17230                     
17231                 ];
17232
17233         } else {
17234             
17235                 Roo.log(" no label && no align");
17236                 cfg.cn = [  inputblock ] ;
17237                 
17238                 
17239         }
17240         if(this.boxLabel){
17241              var boxLabelCfg = {
17242                 tag: 'label',
17243                 //'for': id, // box label is handled by onclick - so no for...
17244                 cls: 'box-label',
17245                 html: this.boxLabel
17246             }
17247             
17248             if(this.tooltip){
17249                 boxLabelCfg.tooltip = this.tooltip;
17250             }
17251              
17252             cfg.cn.push(boxLabelCfg);
17253         }
17254         
17255         
17256        
17257         return cfg;
17258         
17259     },
17260     
17261     /**
17262      * return the real input element.
17263      */
17264     inputEl: function ()
17265     {
17266         return this.el.select('input.roo-' + this.inputType,true).first();
17267     },
17268     
17269     labelEl: function()
17270     {
17271         return this.el.select('label.control-label',true).first();
17272     },
17273     /* depricated... */
17274     
17275     label: function()
17276     {
17277         return this.labelEl();
17278     },
17279     
17280     initEvents : function()
17281     {
17282 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17283         
17284         this.inputEl().on('click', this.onClick,  this);
17285         
17286         if (this.boxLabel) { 
17287             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17288         }
17289         
17290         this.startValue = this.getValue();
17291         
17292         if(this.groupId){
17293             Roo.bootstrap.CheckBox.register(this);
17294         }
17295     },
17296     
17297     onClick : function()
17298     {   
17299         this.setChecked(!this.checked);
17300     },
17301     
17302     setChecked : function(state,suppressEvent)
17303     {
17304         this.startValue = this.getValue();
17305         
17306         if(this.inputType == 'radio'){
17307             
17308             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17309                 e.dom.checked = false;
17310             });
17311             
17312             this.inputEl().dom.checked = true;
17313             
17314             this.inputEl().dom.value = this.inputValue;
17315             
17316             if(suppressEvent !== true){
17317                 this.fireEvent('check', this, true);
17318             }
17319             
17320             this.validate();
17321             
17322             return;
17323         }
17324         
17325         this.checked = state;
17326         
17327         this.inputEl().dom.checked = state;
17328         
17329         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17330         
17331         if(suppressEvent !== true){
17332             this.fireEvent('check', this, state);
17333         }
17334         
17335         this.validate();
17336     },
17337     
17338     getValue : function()
17339     {
17340         if(this.inputType == 'radio'){
17341             return this.getGroupValue();
17342         }
17343         
17344         return this.inputEl().getValue();
17345         
17346     },
17347     
17348     getGroupValue : function()
17349     {
17350         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17351             return '';
17352         }
17353         
17354         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17355     },
17356     
17357     setValue : function(v,suppressEvent)
17358     {
17359         if(this.inputType == 'radio'){
17360             this.setGroupValue(v, suppressEvent);
17361             return;
17362         }
17363         
17364         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17365         
17366         this.validate();
17367     },
17368     
17369     setGroupValue : function(v, suppressEvent)
17370     {
17371         this.startValue = this.getValue();
17372         
17373         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17374             e.dom.checked = false;
17375             
17376             if(e.dom.value == v){
17377                 e.dom.checked = true;
17378             }
17379         });
17380         
17381         if(suppressEvent !== true){
17382             this.fireEvent('check', this, true);
17383         }
17384
17385         this.validate();
17386         
17387         return;
17388     },
17389     
17390     validate : function()
17391     {
17392         if(
17393                 this.disabled || 
17394                 (this.inputType == 'radio' && this.validateRadio()) ||
17395                 (this.inputType == 'checkbox' && this.validateCheckbox())
17396         ){
17397             this.markValid();
17398             return true;
17399         }
17400         
17401         this.markInvalid();
17402         return false;
17403     },
17404     
17405     validateRadio : function()
17406     {
17407         var valid = false;
17408         
17409         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17410             if(!e.dom.checked){
17411                 return;
17412             }
17413             
17414             valid = true;
17415             
17416             return false;
17417         });
17418         
17419         return valid;
17420     },
17421     
17422     validateCheckbox : function()
17423     {
17424         if(!this.groupId){
17425             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17426         }
17427         
17428         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17429         
17430         if(!group){
17431             return false;
17432         }
17433         
17434         var r = false;
17435         
17436         for(var i in group){
17437             if(r){
17438                 break;
17439             }
17440             
17441             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17442         }
17443         
17444         return r;
17445     },
17446     
17447     /**
17448      * Mark this field as valid
17449      */
17450     markValid : function()
17451     {
17452         if(this.allowBlank){
17453             return;
17454         }
17455         
17456         var _this = this;
17457         
17458         this.fireEvent('valid', this);
17459         
17460         if(this.inputType == 'radio'){
17461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17462                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17463                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17464             });
17465             
17466             return;
17467         }
17468         
17469         if(!this.groupId){
17470             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17471             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17472             return;
17473         }
17474         
17475         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17476             
17477         if(!group){
17478             return;
17479         }
17480         
17481         for(var i in group){
17482             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17483             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17484         }
17485     },
17486     
17487      /**
17488      * Mark this field as invalid
17489      * @param {String} msg The validation message
17490      */
17491     markInvalid : function(msg)
17492     {
17493         if(this.allowBlank){
17494             return;
17495         }
17496         
17497         var _this = this;
17498         
17499         this.fireEvent('invalid', this, msg);
17500         
17501         if(this.inputType == 'radio'){
17502             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17503                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17504                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17505             });
17506             
17507             return;
17508         }
17509         
17510         if(!this.groupId){
17511             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17512             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17513             return;
17514         }
17515         
17516         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17517             
17518         if(!group){
17519             return;
17520         }
17521         
17522         for(var i in group){
17523             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17524             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17525         }
17526         
17527     }
17528     
17529 });
17530
17531 Roo.apply(Roo.bootstrap.CheckBox, {
17532     
17533     groups: {},
17534     
17535      /**
17536     * register a CheckBox Group
17537     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17538     */
17539     register : function(checkbox)
17540     {
17541         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17542             this.groups[checkbox.groupId] = {};
17543         }
17544         
17545         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17546             return;
17547         }
17548         
17549         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17550         
17551     },
17552     /**
17553     * fetch a CheckBox Group based on the group ID
17554     * @param {string} the group ID
17555     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17556     */
17557     get: function(groupId) {
17558         if (typeof(this.groups[groupId]) == 'undefined') {
17559             return false;
17560         }
17561         
17562         return this.groups[groupId] ;
17563     }
17564     
17565     
17566 });
17567 /*
17568  * - LGPL
17569  *
17570  * Radio
17571  *
17572  *
17573  * not inline
17574  *<div class="radio">
17575   <label>
17576     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17577     Option one is this and that&mdash;be sure to include why it's great
17578   </label>
17579 </div>
17580  *
17581  *
17582  *inline
17583  *<span>
17584  *<label class="radio-inline">fieldLabel</label>
17585  *<label class="radio-inline">
17586   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17587 </label>
17588 <span>
17589  * 
17590  * 
17591  */
17592
17593 /**
17594  * @class Roo.bootstrap.Radio
17595  * @extends Roo.bootstrap.CheckBox
17596  * Bootstrap Radio class
17597
17598  * @constructor
17599  * Create a new Radio
17600  * @param {Object} config The config object
17601  */
17602
17603 Roo.bootstrap.Radio = function(config){
17604     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17605    
17606 };
17607
17608 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17609     
17610     inputType: 'radio',
17611     inputValue: '',
17612     valueOff: '',
17613     
17614     getAutoCreate : function()
17615     {
17616         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17617         align = align || 'left'; // default...
17618         
17619         
17620         
17621         var id = Roo.id();
17622         
17623         var cfg = {
17624                 tag : this.inline ? 'span' : 'div',
17625                 cls : '',
17626                 cn : []
17627         };
17628         
17629         var inline = this.inline ? ' radio-inline' : '';
17630         
17631         var lbl = {
17632                 tag: 'label' ,
17633                 // does not need for, as we wrap the input with it..
17634                 'for' : id,
17635                 cls : 'control-label box-label' + inline,
17636                 cn : []
17637         };
17638         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17639         
17640         var fieldLabel = {
17641             tag: 'label' ,
17642             //cls : 'control-label' + inline,
17643             html : this.fieldLabel,
17644             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17645         };
17646         
17647  
17648         
17649         
17650         var input =  {
17651             tag: 'input',
17652             id : id,
17653             type : this.inputType,
17654             //value : (!this.checked) ? this.valueOff : this.inputValue,
17655             value : this.inputValue,
17656             cls : 'roo-radio',
17657             placeholder : this.placeholder || '' // ?? needed????
17658             
17659         };
17660         if (this.weight) { // Validity check?
17661             input.cls += " radio-" + this.weight;
17662         }
17663         if (this.disabled) {
17664             input.disabled=true;
17665         }
17666         
17667         if(this.checked){
17668             input.checked = this.checked;
17669         }
17670         
17671         if (this.name) {
17672             input.name = this.name;
17673         }
17674         
17675         if (this.size) {
17676             input.cls += ' input-' + this.size;
17677         }
17678         
17679         //?? can span's inline have a width??
17680         
17681         var settings=this;
17682         ['xs','sm','md','lg'].map(function(size){
17683             if (settings[size]) {
17684                 cfg.cls += ' col-' + size + '-' + settings[size];
17685             }
17686         });
17687         
17688         var inputblock = input;
17689         
17690         if (this.before || this.after) {
17691             
17692             inputblock = {
17693                 cls : 'input-group',
17694                 tag : 'span',
17695                 cn :  [] 
17696             };
17697             if (this.before) {
17698                 inputblock.cn.push({
17699                     tag :'span',
17700                     cls : 'input-group-addon',
17701                     html : this.before
17702                 });
17703             }
17704             inputblock.cn.push(input);
17705             if (this.after) {
17706                 inputblock.cn.push({
17707                     tag :'span',
17708                     cls : 'input-group-addon',
17709                     html : this.after
17710                 });
17711             }
17712             
17713         };
17714         
17715         
17716         if (this.fieldLabel && this.fieldLabel.length) {
17717             cfg.cn.push(fieldLabel);
17718         }
17719        
17720         // normal bootstrap puts the input inside the label.
17721         // however with our styled version - it has to go after the input.
17722        
17723         //lbl.cn.push(inputblock);
17724         
17725         var lblwrap =  {
17726             tag: 'span',
17727             cls: 'radio' + inline,
17728             cn: [
17729                 inputblock,
17730                 lbl
17731             ]
17732         };
17733         
17734         cfg.cn.push( lblwrap);
17735         
17736         if(this.boxLabel){
17737             lbl.cn.push({
17738                 tag: 'span',
17739                 html: this.boxLabel
17740             })
17741         }
17742          
17743         
17744         return cfg;
17745         
17746     },
17747     
17748     initEvents : function()
17749     {
17750 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17751         
17752         this.inputEl().on('click', this.onClick,  this);
17753         if (this.boxLabel) {
17754             Roo.log('find label')
17755             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17756         }
17757         
17758     },
17759     
17760     inputEl: function ()
17761     {
17762         return this.el.select('input.roo-radio',true).first();
17763     },
17764     onClick : function()
17765     {   
17766         Roo.log("click");
17767         this.setChecked(true);
17768     },
17769     
17770     setChecked : function(state,suppressEvent)
17771     {
17772         if(state){
17773             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17774                 v.dom.checked = false;
17775             });
17776         }
17777         Roo.log(this.inputEl().dom);
17778         this.checked = state;
17779         this.inputEl().dom.checked = state;
17780         
17781         if(suppressEvent !== true){
17782             this.fireEvent('check', this, state);
17783         }
17784         
17785         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17786         
17787     },
17788     
17789     getGroupValue : function()
17790     {
17791         var value = '';
17792         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17793             if(v.dom.checked == true){
17794                 value = v.dom.value;
17795             }
17796         });
17797         
17798         return value;
17799     },
17800     
17801     /**
17802      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17803      * @return {Mixed} value The field value
17804      */
17805     getValue : function(){
17806         return this.getGroupValue();
17807     }
17808     
17809 });
17810
17811  
17812 //<script type="text/javascript">
17813
17814 /*
17815  * Based  Ext JS Library 1.1.1
17816  * Copyright(c) 2006-2007, Ext JS, LLC.
17817  * LGPL
17818  *
17819  */
17820  
17821 /**
17822  * @class Roo.HtmlEditorCore
17823  * @extends Roo.Component
17824  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17825  *
17826  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17827  */
17828
17829 Roo.HtmlEditorCore = function(config){
17830     
17831     
17832     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17833     
17834     
17835     this.addEvents({
17836         /**
17837          * @event initialize
17838          * Fires when the editor is fully initialized (including the iframe)
17839          * @param {Roo.HtmlEditorCore} this
17840          */
17841         initialize: true,
17842         /**
17843          * @event activate
17844          * Fires when the editor is first receives the focus. Any insertion must wait
17845          * until after this event.
17846          * @param {Roo.HtmlEditorCore} this
17847          */
17848         activate: true,
17849          /**
17850          * @event beforesync
17851          * Fires before the textarea is updated with content from the editor iframe. Return false
17852          * to cancel the sync.
17853          * @param {Roo.HtmlEditorCore} this
17854          * @param {String} html
17855          */
17856         beforesync: true,
17857          /**
17858          * @event beforepush
17859          * Fires before the iframe editor is updated with content from the textarea. Return false
17860          * to cancel the push.
17861          * @param {Roo.HtmlEditorCore} this
17862          * @param {String} html
17863          */
17864         beforepush: true,
17865          /**
17866          * @event sync
17867          * Fires when the textarea is updated with content from the editor iframe.
17868          * @param {Roo.HtmlEditorCore} this
17869          * @param {String} html
17870          */
17871         sync: true,
17872          /**
17873          * @event push
17874          * Fires when the iframe editor is updated with content from the textarea.
17875          * @param {Roo.HtmlEditorCore} this
17876          * @param {String} html
17877          */
17878         push: true,
17879         
17880         /**
17881          * @event editorevent
17882          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17883          * @param {Roo.HtmlEditorCore} this
17884          */
17885         editorevent: true
17886         
17887     });
17888     
17889     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17890     
17891     // defaults : white / black...
17892     this.applyBlacklists();
17893     
17894     
17895     
17896 };
17897
17898
17899 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17900
17901
17902      /**
17903      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17904      */
17905     
17906     owner : false,
17907     
17908      /**
17909      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17910      *                        Roo.resizable.
17911      */
17912     resizable : false,
17913      /**
17914      * @cfg {Number} height (in pixels)
17915      */   
17916     height: 300,
17917    /**
17918      * @cfg {Number} width (in pixels)
17919      */   
17920     width: 500,
17921     
17922     /**
17923      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17924      * 
17925      */
17926     stylesheets: false,
17927     
17928     // id of frame..
17929     frameId: false,
17930     
17931     // private properties
17932     validationEvent : false,
17933     deferHeight: true,
17934     initialized : false,
17935     activated : false,
17936     sourceEditMode : false,
17937     onFocus : Roo.emptyFn,
17938     iframePad:3,
17939     hideMode:'offsets',
17940     
17941     clearUp: true,
17942     
17943     // blacklist + whitelisted elements..
17944     black: false,
17945     white: false,
17946      
17947     
17948
17949     /**
17950      * Protected method that will not generally be called directly. It
17951      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17952      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17953      */
17954     getDocMarkup : function(){
17955         // body styles..
17956         var st = '';
17957         
17958         // inherit styels from page...?? 
17959         if (this.stylesheets === false) {
17960             
17961             Roo.get(document.head).select('style').each(function(node) {
17962                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17963             });
17964             
17965             Roo.get(document.head).select('link').each(function(node) { 
17966                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17967             });
17968             
17969         } else if (!this.stylesheets.length) {
17970                 // simple..
17971                 st = '<style type="text/css">' +
17972                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17973                    '</style>';
17974         } else { 
17975             
17976         }
17977         
17978         st +=  '<style type="text/css">' +
17979             'IMG { cursor: pointer } ' +
17980         '</style>';
17981
17982         
17983         return '<html><head>' + st  +
17984             //<style type="text/css">' +
17985             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17986             //'</style>' +
17987             ' </head><body class="roo-htmleditor-body"></body></html>';
17988     },
17989
17990     // private
17991     onRender : function(ct, position)
17992     {
17993         var _t = this;
17994         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17995         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17996         
17997         
17998         this.el.dom.style.border = '0 none';
17999         this.el.dom.setAttribute('tabIndex', -1);
18000         this.el.addClass('x-hidden hide');
18001         
18002         
18003         
18004         if(Roo.isIE){ // fix IE 1px bogus margin
18005             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18006         }
18007        
18008         
18009         this.frameId = Roo.id();
18010         
18011          
18012         
18013         var iframe = this.owner.wrap.createChild({
18014             tag: 'iframe',
18015             cls: 'form-control', // bootstrap..
18016             id: this.frameId,
18017             name: this.frameId,
18018             frameBorder : 'no',
18019             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18020         }, this.el
18021         );
18022         
18023         
18024         this.iframe = iframe.dom;
18025
18026          this.assignDocWin();
18027         
18028         this.doc.designMode = 'on';
18029        
18030         this.doc.open();
18031         this.doc.write(this.getDocMarkup());
18032         this.doc.close();
18033
18034         
18035         var task = { // must defer to wait for browser to be ready
18036             run : function(){
18037                 //console.log("run task?" + this.doc.readyState);
18038                 this.assignDocWin();
18039                 if(this.doc.body || this.doc.readyState == 'complete'){
18040                     try {
18041                         this.doc.designMode="on";
18042                     } catch (e) {
18043                         return;
18044                     }
18045                     Roo.TaskMgr.stop(task);
18046                     this.initEditor.defer(10, this);
18047                 }
18048             },
18049             interval : 10,
18050             duration: 10000,
18051             scope: this
18052         };
18053         Roo.TaskMgr.start(task);
18054
18055     },
18056
18057     // private
18058     onResize : function(w, h)
18059     {
18060          Roo.log('resize: ' +w + ',' + h );
18061         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18062         if(!this.iframe){
18063             return;
18064         }
18065         if(typeof w == 'number'){
18066             
18067             this.iframe.style.width = w + 'px';
18068         }
18069         if(typeof h == 'number'){
18070             
18071             this.iframe.style.height = h + 'px';
18072             if(this.doc){
18073                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18074             }
18075         }
18076         
18077     },
18078
18079     /**
18080      * Toggles the editor between standard and source edit mode.
18081      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18082      */
18083     toggleSourceEdit : function(sourceEditMode){
18084         
18085         this.sourceEditMode = sourceEditMode === true;
18086         
18087         if(this.sourceEditMode){
18088  
18089             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18090             
18091         }else{
18092             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18093             //this.iframe.className = '';
18094             this.deferFocus();
18095         }
18096         //this.setSize(this.owner.wrap.getSize());
18097         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18098     },
18099
18100     
18101   
18102
18103     /**
18104      * Protected method that will not generally be called directly. If you need/want
18105      * custom HTML cleanup, this is the method you should override.
18106      * @param {String} html The HTML to be cleaned
18107      * return {String} The cleaned HTML
18108      */
18109     cleanHtml : function(html){
18110         html = String(html);
18111         if(html.length > 5){
18112             if(Roo.isSafari){ // strip safari nonsense
18113                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18114             }
18115         }
18116         if(html == '&nbsp;'){
18117             html = '';
18118         }
18119         return html;
18120     },
18121
18122     /**
18123      * HTML Editor -> Textarea
18124      * Protected method that will not generally be called directly. Syncs the contents
18125      * of the editor iframe with the textarea.
18126      */
18127     syncValue : function(){
18128         if(this.initialized){
18129             var bd = (this.doc.body || this.doc.documentElement);
18130             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18131             var html = bd.innerHTML;
18132             if(Roo.isSafari){
18133                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18134                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18135                 if(m && m[1]){
18136                     html = '<div style="'+m[0]+'">' + html + '</div>';
18137                 }
18138             }
18139             html = this.cleanHtml(html);
18140             // fix up the special chars.. normaly like back quotes in word...
18141             // however we do not want to do this with chinese..
18142             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18143                 var cc = b.charCodeAt();
18144                 if (
18145                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18146                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18147                     (cc >= 0xf900 && cc < 0xfb00 )
18148                 ) {
18149                         return b;
18150                 }
18151                 return "&#"+cc+";" 
18152             });
18153             if(this.owner.fireEvent('beforesync', this, html) !== false){
18154                 this.el.dom.value = html;
18155                 this.owner.fireEvent('sync', this, html);
18156             }
18157         }
18158     },
18159
18160     /**
18161      * Protected method that will not generally be called directly. Pushes the value of the textarea
18162      * into the iframe editor.
18163      */
18164     pushValue : function(){
18165         if(this.initialized){
18166             var v = this.el.dom.value.trim();
18167             
18168 //            if(v.length < 1){
18169 //                v = '&#160;';
18170 //            }
18171             
18172             if(this.owner.fireEvent('beforepush', this, v) !== false){
18173                 var d = (this.doc.body || this.doc.documentElement);
18174                 d.innerHTML = v;
18175                 this.cleanUpPaste();
18176                 this.el.dom.value = d.innerHTML;
18177                 this.owner.fireEvent('push', this, v);
18178             }
18179         }
18180     },
18181
18182     // private
18183     deferFocus : function(){
18184         this.focus.defer(10, this);
18185     },
18186
18187     // doc'ed in Field
18188     focus : function(){
18189         if(this.win && !this.sourceEditMode){
18190             this.win.focus();
18191         }else{
18192             this.el.focus();
18193         }
18194     },
18195     
18196     assignDocWin: function()
18197     {
18198         var iframe = this.iframe;
18199         
18200          if(Roo.isIE){
18201             this.doc = iframe.contentWindow.document;
18202             this.win = iframe.contentWindow;
18203         } else {
18204 //            if (!Roo.get(this.frameId)) {
18205 //                return;
18206 //            }
18207 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18208 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18209             
18210             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18211                 return;
18212             }
18213             
18214             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18215             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18216         }
18217     },
18218     
18219     // private
18220     initEditor : function(){
18221         //console.log("INIT EDITOR");
18222         this.assignDocWin();
18223         
18224         
18225         
18226         this.doc.designMode="on";
18227         this.doc.open();
18228         this.doc.write(this.getDocMarkup());
18229         this.doc.close();
18230         
18231         var dbody = (this.doc.body || this.doc.documentElement);
18232         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18233         // this copies styles from the containing element into thsi one..
18234         // not sure why we need all of this..
18235         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18236         
18237         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18238         //ss['background-attachment'] = 'fixed'; // w3c
18239         dbody.bgProperties = 'fixed'; // ie
18240         //Roo.DomHelper.applyStyles(dbody, ss);
18241         Roo.EventManager.on(this.doc, {
18242             //'mousedown': this.onEditorEvent,
18243             'mouseup': this.onEditorEvent,
18244             'dblclick': this.onEditorEvent,
18245             'click': this.onEditorEvent,
18246             'keyup': this.onEditorEvent,
18247             buffer:100,
18248             scope: this
18249         });
18250         if(Roo.isGecko){
18251             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18252         }
18253         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18254             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18255         }
18256         this.initialized = true;
18257
18258         this.owner.fireEvent('initialize', this);
18259         this.pushValue();
18260     },
18261
18262     // private
18263     onDestroy : function(){
18264         
18265         
18266         
18267         if(this.rendered){
18268             
18269             //for (var i =0; i < this.toolbars.length;i++) {
18270             //    // fixme - ask toolbars for heights?
18271             //    this.toolbars[i].onDestroy();
18272            // }
18273             
18274             //this.wrap.dom.innerHTML = '';
18275             //this.wrap.remove();
18276         }
18277     },
18278
18279     // private
18280     onFirstFocus : function(){
18281         
18282         this.assignDocWin();
18283         
18284         
18285         this.activated = true;
18286          
18287     
18288         if(Roo.isGecko){ // prevent silly gecko errors
18289             this.win.focus();
18290             var s = this.win.getSelection();
18291             if(!s.focusNode || s.focusNode.nodeType != 3){
18292                 var r = s.getRangeAt(0);
18293                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18294                 r.collapse(true);
18295                 this.deferFocus();
18296             }
18297             try{
18298                 this.execCmd('useCSS', true);
18299                 this.execCmd('styleWithCSS', false);
18300             }catch(e){}
18301         }
18302         this.owner.fireEvent('activate', this);
18303     },
18304
18305     // private
18306     adjustFont: function(btn){
18307         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18308         //if(Roo.isSafari){ // safari
18309         //    adjust *= 2;
18310        // }
18311         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18312         if(Roo.isSafari){ // safari
18313             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18314             v =  (v < 10) ? 10 : v;
18315             v =  (v > 48) ? 48 : v;
18316             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18317             
18318         }
18319         
18320         
18321         v = Math.max(1, v+adjust);
18322         
18323         this.execCmd('FontSize', v  );
18324     },
18325
18326     onEditorEvent : function(e){
18327         this.owner.fireEvent('editorevent', this, e);
18328       //  this.updateToolbar();
18329         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18330     },
18331
18332     insertTag : function(tg)
18333     {
18334         // could be a bit smarter... -> wrap the current selected tRoo..
18335         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18336             
18337             range = this.createRange(this.getSelection());
18338             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18339             wrappingNode.appendChild(range.extractContents());
18340             range.insertNode(wrappingNode);
18341
18342             return;
18343             
18344             
18345             
18346         }
18347         this.execCmd("formatblock",   tg);
18348         
18349     },
18350     
18351     insertText : function(txt)
18352     {
18353         
18354         
18355         var range = this.createRange();
18356         range.deleteContents();
18357                //alert(Sender.getAttribute('label'));
18358                
18359         range.insertNode(this.doc.createTextNode(txt));
18360     } ,
18361     
18362      
18363
18364     /**
18365      * Executes a Midas editor command on the editor document and performs necessary focus and
18366      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18367      * @param {String} cmd The Midas command
18368      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18369      */
18370     relayCmd : function(cmd, value){
18371         this.win.focus();
18372         this.execCmd(cmd, value);
18373         this.owner.fireEvent('editorevent', this);
18374         //this.updateToolbar();
18375         this.owner.deferFocus();
18376     },
18377
18378     /**
18379      * Executes a Midas editor command directly on the editor document.
18380      * For visual commands, you should use {@link #relayCmd} instead.
18381      * <b>This should only be called after the editor is initialized.</b>
18382      * @param {String} cmd The Midas command
18383      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18384      */
18385     execCmd : function(cmd, value){
18386         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18387         this.syncValue();
18388     },
18389  
18390  
18391    
18392     /**
18393      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18394      * to insert tRoo.
18395      * @param {String} text | dom node.. 
18396      */
18397     insertAtCursor : function(text)
18398     {
18399         
18400         
18401         
18402         if(!this.activated){
18403             return;
18404         }
18405         /*
18406         if(Roo.isIE){
18407             this.win.focus();
18408             var r = this.doc.selection.createRange();
18409             if(r){
18410                 r.collapse(true);
18411                 r.pasteHTML(text);
18412                 this.syncValue();
18413                 this.deferFocus();
18414             
18415             }
18416             return;
18417         }
18418         */
18419         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18420             this.win.focus();
18421             
18422             
18423             // from jquery ui (MIT licenced)
18424             var range, node;
18425             var win = this.win;
18426             
18427             if (win.getSelection && win.getSelection().getRangeAt) {
18428                 range = win.getSelection().getRangeAt(0);
18429                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18430                 range.insertNode(node);
18431             } else if (win.document.selection && win.document.selection.createRange) {
18432                 // no firefox support
18433                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18434                 win.document.selection.createRange().pasteHTML(txt);
18435             } else {
18436                 // no firefox support
18437                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18438                 this.execCmd('InsertHTML', txt);
18439             } 
18440             
18441             this.syncValue();
18442             
18443             this.deferFocus();
18444         }
18445     },
18446  // private
18447     mozKeyPress : function(e){
18448         if(e.ctrlKey){
18449             var c = e.getCharCode(), cmd;
18450           
18451             if(c > 0){
18452                 c = String.fromCharCode(c).toLowerCase();
18453                 switch(c){
18454                     case 'b':
18455                         cmd = 'bold';
18456                         break;
18457                     case 'i':
18458                         cmd = 'italic';
18459                         break;
18460                     
18461                     case 'u':
18462                         cmd = 'underline';
18463                         break;
18464                     
18465                     case 'v':
18466                         this.cleanUpPaste.defer(100, this);
18467                         return;
18468                         
18469                 }
18470                 if(cmd){
18471                     this.win.focus();
18472                     this.execCmd(cmd);
18473                     this.deferFocus();
18474                     e.preventDefault();
18475                 }
18476                 
18477             }
18478         }
18479     },
18480
18481     // private
18482     fixKeys : function(){ // load time branching for fastest keydown performance
18483         if(Roo.isIE){
18484             return function(e){
18485                 var k = e.getKey(), r;
18486                 if(k == e.TAB){
18487                     e.stopEvent();
18488                     r = this.doc.selection.createRange();
18489                     if(r){
18490                         r.collapse(true);
18491                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18492                         this.deferFocus();
18493                     }
18494                     return;
18495                 }
18496                 
18497                 if(k == e.ENTER){
18498                     r = this.doc.selection.createRange();
18499                     if(r){
18500                         var target = r.parentElement();
18501                         if(!target || target.tagName.toLowerCase() != 'li'){
18502                             e.stopEvent();
18503                             r.pasteHTML('<br />');
18504                             r.collapse(false);
18505                             r.select();
18506                         }
18507                     }
18508                 }
18509                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18510                     this.cleanUpPaste.defer(100, this);
18511                     return;
18512                 }
18513                 
18514                 
18515             };
18516         }else if(Roo.isOpera){
18517             return function(e){
18518                 var k = e.getKey();
18519                 if(k == e.TAB){
18520                     e.stopEvent();
18521                     this.win.focus();
18522                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18523                     this.deferFocus();
18524                 }
18525                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18526                     this.cleanUpPaste.defer(100, this);
18527                     return;
18528                 }
18529                 
18530             };
18531         }else if(Roo.isSafari){
18532             return function(e){
18533                 var k = e.getKey();
18534                 
18535                 if(k == e.TAB){
18536                     e.stopEvent();
18537                     this.execCmd('InsertText','\t');
18538                     this.deferFocus();
18539                     return;
18540                 }
18541                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18542                     this.cleanUpPaste.defer(100, this);
18543                     return;
18544                 }
18545                 
18546              };
18547         }
18548     }(),
18549     
18550     getAllAncestors: function()
18551     {
18552         var p = this.getSelectedNode();
18553         var a = [];
18554         if (!p) {
18555             a.push(p); // push blank onto stack..
18556             p = this.getParentElement();
18557         }
18558         
18559         
18560         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18561             a.push(p);
18562             p = p.parentNode;
18563         }
18564         a.push(this.doc.body);
18565         return a;
18566     },
18567     lastSel : false,
18568     lastSelNode : false,
18569     
18570     
18571     getSelection : function() 
18572     {
18573         this.assignDocWin();
18574         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18575     },
18576     
18577     getSelectedNode: function() 
18578     {
18579         // this may only work on Gecko!!!
18580         
18581         // should we cache this!!!!
18582         
18583         
18584         
18585          
18586         var range = this.createRange(this.getSelection()).cloneRange();
18587         
18588         if (Roo.isIE) {
18589             var parent = range.parentElement();
18590             while (true) {
18591                 var testRange = range.duplicate();
18592                 testRange.moveToElementText(parent);
18593                 if (testRange.inRange(range)) {
18594                     break;
18595                 }
18596                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18597                     break;
18598                 }
18599                 parent = parent.parentElement;
18600             }
18601             return parent;
18602         }
18603         
18604         // is ancestor a text element.
18605         var ac =  range.commonAncestorContainer;
18606         if (ac.nodeType == 3) {
18607             ac = ac.parentNode;
18608         }
18609         
18610         var ar = ac.childNodes;
18611          
18612         var nodes = [];
18613         var other_nodes = [];
18614         var has_other_nodes = false;
18615         for (var i=0;i<ar.length;i++) {
18616             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18617                 continue;
18618             }
18619             // fullly contained node.
18620             
18621             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18622                 nodes.push(ar[i]);
18623                 continue;
18624             }
18625             
18626             // probably selected..
18627             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18628                 other_nodes.push(ar[i]);
18629                 continue;
18630             }
18631             // outer..
18632             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18633                 continue;
18634             }
18635             
18636             
18637             has_other_nodes = true;
18638         }
18639         if (!nodes.length && other_nodes.length) {
18640             nodes= other_nodes;
18641         }
18642         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18643             return false;
18644         }
18645         
18646         return nodes[0];
18647     },
18648     createRange: function(sel)
18649     {
18650         // this has strange effects when using with 
18651         // top toolbar - not sure if it's a great idea.
18652         //this.editor.contentWindow.focus();
18653         if (typeof sel != "undefined") {
18654             try {
18655                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18656             } catch(e) {
18657                 return this.doc.createRange();
18658             }
18659         } else {
18660             return this.doc.createRange();
18661         }
18662     },
18663     getParentElement: function()
18664     {
18665         
18666         this.assignDocWin();
18667         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18668         
18669         var range = this.createRange(sel);
18670          
18671         try {
18672             var p = range.commonAncestorContainer;
18673             while (p.nodeType == 3) { // text node
18674                 p = p.parentNode;
18675             }
18676             return p;
18677         } catch (e) {
18678             return null;
18679         }
18680     
18681     },
18682     /***
18683      *
18684      * Range intersection.. the hard stuff...
18685      *  '-1' = before
18686      *  '0' = hits..
18687      *  '1' = after.
18688      *         [ -- selected range --- ]
18689      *   [fail]                        [fail]
18690      *
18691      *    basically..
18692      *      if end is before start or  hits it. fail.
18693      *      if start is after end or hits it fail.
18694      *
18695      *   if either hits (but other is outside. - then it's not 
18696      *   
18697      *    
18698      **/
18699     
18700     
18701     // @see http://www.thismuchiknow.co.uk/?p=64.
18702     rangeIntersectsNode : function(range, node)
18703     {
18704         var nodeRange = node.ownerDocument.createRange();
18705         try {
18706             nodeRange.selectNode(node);
18707         } catch (e) {
18708             nodeRange.selectNodeContents(node);
18709         }
18710     
18711         var rangeStartRange = range.cloneRange();
18712         rangeStartRange.collapse(true);
18713     
18714         var rangeEndRange = range.cloneRange();
18715         rangeEndRange.collapse(false);
18716     
18717         var nodeStartRange = nodeRange.cloneRange();
18718         nodeStartRange.collapse(true);
18719     
18720         var nodeEndRange = nodeRange.cloneRange();
18721         nodeEndRange.collapse(false);
18722     
18723         return rangeStartRange.compareBoundaryPoints(
18724                  Range.START_TO_START, nodeEndRange) == -1 &&
18725                rangeEndRange.compareBoundaryPoints(
18726                  Range.START_TO_START, nodeStartRange) == 1;
18727         
18728          
18729     },
18730     rangeCompareNode : function(range, node)
18731     {
18732         var nodeRange = node.ownerDocument.createRange();
18733         try {
18734             nodeRange.selectNode(node);
18735         } catch (e) {
18736             nodeRange.selectNodeContents(node);
18737         }
18738         
18739         
18740         range.collapse(true);
18741     
18742         nodeRange.collapse(true);
18743      
18744         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18745         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18746          
18747         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18748         
18749         var nodeIsBefore   =  ss == 1;
18750         var nodeIsAfter    = ee == -1;
18751         
18752         if (nodeIsBefore && nodeIsAfter)
18753             return 0; // outer
18754         if (!nodeIsBefore && nodeIsAfter)
18755             return 1; //right trailed.
18756         
18757         if (nodeIsBefore && !nodeIsAfter)
18758             return 2;  // left trailed.
18759         // fully contined.
18760         return 3;
18761     },
18762
18763     // private? - in a new class?
18764     cleanUpPaste :  function()
18765     {
18766         // cleans up the whole document..
18767         Roo.log('cleanuppaste');
18768         
18769         this.cleanUpChildren(this.doc.body);
18770         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18771         if (clean != this.doc.body.innerHTML) {
18772             this.doc.body.innerHTML = clean;
18773         }
18774         
18775     },
18776     
18777     cleanWordChars : function(input) {// change the chars to hex code
18778         var he = Roo.HtmlEditorCore;
18779         
18780         var output = input;
18781         Roo.each(he.swapCodes, function(sw) { 
18782             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18783             
18784             output = output.replace(swapper, sw[1]);
18785         });
18786         
18787         return output;
18788     },
18789     
18790     
18791     cleanUpChildren : function (n)
18792     {
18793         if (!n.childNodes.length) {
18794             return;
18795         }
18796         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18797            this.cleanUpChild(n.childNodes[i]);
18798         }
18799     },
18800     
18801     
18802         
18803     
18804     cleanUpChild : function (node)
18805     {
18806         var ed = this;
18807         //console.log(node);
18808         if (node.nodeName == "#text") {
18809             // clean up silly Windows -- stuff?
18810             return; 
18811         }
18812         if (node.nodeName == "#comment") {
18813             node.parentNode.removeChild(node);
18814             // clean up silly Windows -- stuff?
18815             return; 
18816         }
18817         var lcname = node.tagName.toLowerCase();
18818         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18819         // whitelist of tags..
18820         
18821         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18822             // remove node.
18823             node.parentNode.removeChild(node);
18824             return;
18825             
18826         }
18827         
18828         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18829         
18830         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18831         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18832         
18833         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18834         //    remove_keep_children = true;
18835         //}
18836         
18837         if (remove_keep_children) {
18838             this.cleanUpChildren(node);
18839             // inserts everything just before this node...
18840             while (node.childNodes.length) {
18841                 var cn = node.childNodes[0];
18842                 node.removeChild(cn);
18843                 node.parentNode.insertBefore(cn, node);
18844             }
18845             node.parentNode.removeChild(node);
18846             return;
18847         }
18848         
18849         if (!node.attributes || !node.attributes.length) {
18850             this.cleanUpChildren(node);
18851             return;
18852         }
18853         
18854         function cleanAttr(n,v)
18855         {
18856             
18857             if (v.match(/^\./) || v.match(/^\//)) {
18858                 return;
18859             }
18860             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18861                 return;
18862             }
18863             if (v.match(/^#/)) {
18864                 return;
18865             }
18866 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18867             node.removeAttribute(n);
18868             
18869         }
18870         
18871         var cwhite = this.cwhite;
18872         var cblack = this.cblack;
18873             
18874         function cleanStyle(n,v)
18875         {
18876             if (v.match(/expression/)) { //XSS?? should we even bother..
18877                 node.removeAttribute(n);
18878                 return;
18879             }
18880             
18881             var parts = v.split(/;/);
18882             var clean = [];
18883             
18884             Roo.each(parts, function(p) {
18885                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18886                 if (!p.length) {
18887                     return true;
18888                 }
18889                 var l = p.split(':').shift().replace(/\s+/g,'');
18890                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18891                 
18892                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18893 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18894                     //node.removeAttribute(n);
18895                     return true;
18896                 }
18897                 //Roo.log()
18898                 // only allow 'c whitelisted system attributes'
18899                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18900 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18901                     //node.removeAttribute(n);
18902                     return true;
18903                 }
18904                 
18905                 
18906                  
18907                 
18908                 clean.push(p);
18909                 return true;
18910             });
18911             if (clean.length) { 
18912                 node.setAttribute(n, clean.join(';'));
18913             } else {
18914                 node.removeAttribute(n);
18915             }
18916             
18917         }
18918         
18919         
18920         for (var i = node.attributes.length-1; i > -1 ; i--) {
18921             var a = node.attributes[i];
18922             //console.log(a);
18923             
18924             if (a.name.toLowerCase().substr(0,2)=='on')  {
18925                 node.removeAttribute(a.name);
18926                 continue;
18927             }
18928             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18929                 node.removeAttribute(a.name);
18930                 continue;
18931             }
18932             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18933                 cleanAttr(a.name,a.value); // fixme..
18934                 continue;
18935             }
18936             if (a.name == 'style') {
18937                 cleanStyle(a.name,a.value);
18938                 continue;
18939             }
18940             /// clean up MS crap..
18941             // tecnically this should be a list of valid class'es..
18942             
18943             
18944             if (a.name == 'class') {
18945                 if (a.value.match(/^Mso/)) {
18946                     node.className = '';
18947                 }
18948                 
18949                 if (a.value.match(/body/)) {
18950                     node.className = '';
18951                 }
18952                 continue;
18953             }
18954             
18955             // style cleanup!?
18956             // class cleanup?
18957             
18958         }
18959         
18960         
18961         this.cleanUpChildren(node);
18962         
18963         
18964     },
18965     /**
18966      * Clean up MS wordisms...
18967      */
18968     cleanWord : function(node)
18969     {
18970         var _t = this;
18971         var cleanWordChildren = function()
18972         {
18973             if (!node.childNodes.length) {
18974                 return;
18975             }
18976             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18977                _t.cleanWord(node.childNodes[i]);
18978             }
18979         }
18980         
18981         
18982         if (!node) {
18983             this.cleanWord(this.doc.body);
18984             return;
18985         }
18986         if (node.nodeName == "#text") {
18987             // clean up silly Windows -- stuff?
18988             return; 
18989         }
18990         if (node.nodeName == "#comment") {
18991             node.parentNode.removeChild(node);
18992             // clean up silly Windows -- stuff?
18993             return; 
18994         }
18995         
18996         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18997             node.parentNode.removeChild(node);
18998             return;
18999         }
19000         
19001         // remove - but keep children..
19002         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19003             while (node.childNodes.length) {
19004                 var cn = node.childNodes[0];
19005                 node.removeChild(cn);
19006                 node.parentNode.insertBefore(cn, node);
19007             }
19008             node.parentNode.removeChild(node);
19009             cleanWordChildren();
19010             return;
19011         }
19012         // clean styles
19013         if (node.className.length) {
19014             
19015             var cn = node.className.split(/\W+/);
19016             var cna = [];
19017             Roo.each(cn, function(cls) {
19018                 if (cls.match(/Mso[a-zA-Z]+/)) {
19019                     return;
19020                 }
19021                 cna.push(cls);
19022             });
19023             node.className = cna.length ? cna.join(' ') : '';
19024             if (!cna.length) {
19025                 node.removeAttribute("class");
19026             }
19027         }
19028         
19029         if (node.hasAttribute("lang")) {
19030             node.removeAttribute("lang");
19031         }
19032         
19033         if (node.hasAttribute("style")) {
19034             
19035             var styles = node.getAttribute("style").split(";");
19036             var nstyle = [];
19037             Roo.each(styles, function(s) {
19038                 if (!s.match(/:/)) {
19039                     return;
19040                 }
19041                 var kv = s.split(":");
19042                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19043                     return;
19044                 }
19045                 // what ever is left... we allow.
19046                 nstyle.push(s);
19047             });
19048             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19049             if (!nstyle.length) {
19050                 node.removeAttribute('style');
19051             }
19052         }
19053         
19054         cleanWordChildren();
19055         
19056         
19057     },
19058     domToHTML : function(currentElement, depth, nopadtext) {
19059         
19060         depth = depth || 0;
19061         nopadtext = nopadtext || false;
19062     
19063         if (!currentElement) {
19064             return this.domToHTML(this.doc.body);
19065         }
19066         
19067         //Roo.log(currentElement);
19068         var j;
19069         var allText = false;
19070         var nodeName = currentElement.nodeName;
19071         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19072         
19073         if  (nodeName == '#text') {
19074             
19075             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19076         }
19077         
19078         
19079         var ret = '';
19080         if (nodeName != 'BODY') {
19081              
19082             var i = 0;
19083             // Prints the node tagName, such as <A>, <IMG>, etc
19084             if (tagName) {
19085                 var attr = [];
19086                 for(i = 0; i < currentElement.attributes.length;i++) {
19087                     // quoting?
19088                     var aname = currentElement.attributes.item(i).name;
19089                     if (!currentElement.attributes.item(i).value.length) {
19090                         continue;
19091                     }
19092                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19093                 }
19094                 
19095                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19096             } 
19097             else {
19098                 
19099                 // eack
19100             }
19101         } else {
19102             tagName = false;
19103         }
19104         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19105             return ret;
19106         }
19107         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19108             nopadtext = true;
19109         }
19110         
19111         
19112         // Traverse the tree
19113         i = 0;
19114         var currentElementChild = currentElement.childNodes.item(i);
19115         var allText = true;
19116         var innerHTML  = '';
19117         lastnode = '';
19118         while (currentElementChild) {
19119             // Formatting code (indent the tree so it looks nice on the screen)
19120             var nopad = nopadtext;
19121             if (lastnode == 'SPAN') {
19122                 nopad  = true;
19123             }
19124             // text
19125             if  (currentElementChild.nodeName == '#text') {
19126                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19127                 toadd = nopadtext ? toadd : toadd.trim();
19128                 if (!nopad && toadd.length > 80) {
19129                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19130                 }
19131                 innerHTML  += toadd;
19132                 
19133                 i++;
19134                 currentElementChild = currentElement.childNodes.item(i);
19135                 lastNode = '';
19136                 continue;
19137             }
19138             allText = false;
19139             
19140             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19141                 
19142             // Recursively traverse the tree structure of the child node
19143             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19144             lastnode = currentElementChild.nodeName;
19145             i++;
19146             currentElementChild=currentElement.childNodes.item(i);
19147         }
19148         
19149         ret += innerHTML;
19150         
19151         if (!allText) {
19152                 // The remaining code is mostly for formatting the tree
19153             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19154         }
19155         
19156         
19157         if (tagName) {
19158             ret+= "</"+tagName+">";
19159         }
19160         return ret;
19161         
19162     },
19163         
19164     applyBlacklists : function()
19165     {
19166         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19167         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19168         
19169         this.white = [];
19170         this.black = [];
19171         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19172             if (b.indexOf(tag) > -1) {
19173                 return;
19174             }
19175             this.white.push(tag);
19176             
19177         }, this);
19178         
19179         Roo.each(w, function(tag) {
19180             if (b.indexOf(tag) > -1) {
19181                 return;
19182             }
19183             if (this.white.indexOf(tag) > -1) {
19184                 return;
19185             }
19186             this.white.push(tag);
19187             
19188         }, this);
19189         
19190         
19191         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19192             if (w.indexOf(tag) > -1) {
19193                 return;
19194             }
19195             this.black.push(tag);
19196             
19197         }, this);
19198         
19199         Roo.each(b, function(tag) {
19200             if (w.indexOf(tag) > -1) {
19201                 return;
19202             }
19203             if (this.black.indexOf(tag) > -1) {
19204                 return;
19205             }
19206             this.black.push(tag);
19207             
19208         }, this);
19209         
19210         
19211         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19212         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19213         
19214         this.cwhite = [];
19215         this.cblack = [];
19216         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19217             if (b.indexOf(tag) > -1) {
19218                 return;
19219             }
19220             this.cwhite.push(tag);
19221             
19222         }, this);
19223         
19224         Roo.each(w, function(tag) {
19225             if (b.indexOf(tag) > -1) {
19226                 return;
19227             }
19228             if (this.cwhite.indexOf(tag) > -1) {
19229                 return;
19230             }
19231             this.cwhite.push(tag);
19232             
19233         }, this);
19234         
19235         
19236         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19237             if (w.indexOf(tag) > -1) {
19238                 return;
19239             }
19240             this.cblack.push(tag);
19241             
19242         }, this);
19243         
19244         Roo.each(b, function(tag) {
19245             if (w.indexOf(tag) > -1) {
19246                 return;
19247             }
19248             if (this.cblack.indexOf(tag) > -1) {
19249                 return;
19250             }
19251             this.cblack.push(tag);
19252             
19253         }, this);
19254     },
19255     
19256     setStylesheets : function(stylesheets)
19257     {
19258         if(typeof(stylesheets) == 'string'){
19259             Roo.get(this.iframe.contentDocument.head).createChild({
19260                 tag : 'link',
19261                 rel : 'stylesheet',
19262                 type : 'text/css',
19263                 href : stylesheets
19264             });
19265             
19266             return;
19267         }
19268         var _this = this;
19269      
19270         Roo.each(stylesheets, function(s) {
19271             if(!s.length){
19272                 return;
19273             }
19274             
19275             Roo.get(_this.iframe.contentDocument.head).createChild({
19276                 tag : 'link',
19277                 rel : 'stylesheet',
19278                 type : 'text/css',
19279                 href : s
19280             });
19281         });
19282
19283         
19284     },
19285     
19286     removeStylesheets : function()
19287     {
19288         var _this = this;
19289         
19290         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19291             s.remove();
19292         });
19293     }
19294     
19295     // hide stuff that is not compatible
19296     /**
19297      * @event blur
19298      * @hide
19299      */
19300     /**
19301      * @event change
19302      * @hide
19303      */
19304     /**
19305      * @event focus
19306      * @hide
19307      */
19308     /**
19309      * @event specialkey
19310      * @hide
19311      */
19312     /**
19313      * @cfg {String} fieldClass @hide
19314      */
19315     /**
19316      * @cfg {String} focusClass @hide
19317      */
19318     /**
19319      * @cfg {String} autoCreate @hide
19320      */
19321     /**
19322      * @cfg {String} inputType @hide
19323      */
19324     /**
19325      * @cfg {String} invalidClass @hide
19326      */
19327     /**
19328      * @cfg {String} invalidText @hide
19329      */
19330     /**
19331      * @cfg {String} msgFx @hide
19332      */
19333     /**
19334      * @cfg {String} validateOnBlur @hide
19335      */
19336 });
19337
19338 Roo.HtmlEditorCore.white = [
19339         'area', 'br', 'img', 'input', 'hr', 'wbr',
19340         
19341        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19342        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19343        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19344        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19345        'table',   'ul',         'xmp', 
19346        
19347        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19348       'thead',   'tr', 
19349      
19350       'dir', 'menu', 'ol', 'ul', 'dl',
19351        
19352       'embed',  'object'
19353 ];
19354
19355
19356 Roo.HtmlEditorCore.black = [
19357     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19358         'applet', // 
19359         'base',   'basefont', 'bgsound', 'blink',  'body', 
19360         'frame',  'frameset', 'head',    'html',   'ilayer', 
19361         'iframe', 'layer',  'link',     'meta',    'object',   
19362         'script', 'style' ,'title',  'xml' // clean later..
19363 ];
19364 Roo.HtmlEditorCore.clean = [
19365     'script', 'style', 'title', 'xml'
19366 ];
19367 Roo.HtmlEditorCore.remove = [
19368     'font'
19369 ];
19370 // attributes..
19371
19372 Roo.HtmlEditorCore.ablack = [
19373     'on'
19374 ];
19375     
19376 Roo.HtmlEditorCore.aclean = [ 
19377     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19378 ];
19379
19380 // protocols..
19381 Roo.HtmlEditorCore.pwhite= [
19382         'http',  'https',  'mailto'
19383 ];
19384
19385 // white listed style attributes.
19386 Roo.HtmlEditorCore.cwhite= [
19387       //  'text-align', /// default is to allow most things..
19388       
19389          
19390 //        'font-size'//??
19391 ];
19392
19393 // black listed style attributes.
19394 Roo.HtmlEditorCore.cblack= [
19395       //  'font-size' -- this can be set by the project 
19396 ];
19397
19398
19399 Roo.HtmlEditorCore.swapCodes   =[ 
19400     [    8211, "--" ], 
19401     [    8212, "--" ], 
19402     [    8216,  "'" ],  
19403     [    8217, "'" ],  
19404     [    8220, '"' ],  
19405     [    8221, '"' ],  
19406     [    8226, "*" ],  
19407     [    8230, "..." ]
19408 ]; 
19409
19410     /*
19411  * - LGPL
19412  *
19413  * HtmlEditor
19414  * 
19415  */
19416
19417 /**
19418  * @class Roo.bootstrap.HtmlEditor
19419  * @extends Roo.bootstrap.TextArea
19420  * Bootstrap HtmlEditor class
19421
19422  * @constructor
19423  * Create a new HtmlEditor
19424  * @param {Object} config The config object
19425  */
19426
19427 Roo.bootstrap.HtmlEditor = function(config){
19428     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19429     if (!this.toolbars) {
19430         this.toolbars = [];
19431     }
19432     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19433     this.addEvents({
19434             /**
19435              * @event initialize
19436              * Fires when the editor is fully initialized (including the iframe)
19437              * @param {HtmlEditor} this
19438              */
19439             initialize: true,
19440             /**
19441              * @event activate
19442              * Fires when the editor is first receives the focus. Any insertion must wait
19443              * until after this event.
19444              * @param {HtmlEditor} this
19445              */
19446             activate: true,
19447              /**
19448              * @event beforesync
19449              * Fires before the textarea is updated with content from the editor iframe. Return false
19450              * to cancel the sync.
19451              * @param {HtmlEditor} this
19452              * @param {String} html
19453              */
19454             beforesync: true,
19455              /**
19456              * @event beforepush
19457              * Fires before the iframe editor is updated with content from the textarea. Return false
19458              * to cancel the push.
19459              * @param {HtmlEditor} this
19460              * @param {String} html
19461              */
19462             beforepush: true,
19463              /**
19464              * @event sync
19465              * Fires when the textarea is updated with content from the editor iframe.
19466              * @param {HtmlEditor} this
19467              * @param {String} html
19468              */
19469             sync: true,
19470              /**
19471              * @event push
19472              * Fires when the iframe editor is updated with content from the textarea.
19473              * @param {HtmlEditor} this
19474              * @param {String} html
19475              */
19476             push: true,
19477              /**
19478              * @event editmodechange
19479              * Fires when the editor switches edit modes
19480              * @param {HtmlEditor} this
19481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19482              */
19483             editmodechange: true,
19484             /**
19485              * @event editorevent
19486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19487              * @param {HtmlEditor} this
19488              */
19489             editorevent: true,
19490             /**
19491              * @event firstfocus
19492              * Fires when on first focus - needed by toolbars..
19493              * @param {HtmlEditor} this
19494              */
19495             firstfocus: true,
19496             /**
19497              * @event autosave
19498              * Auto save the htmlEditor value as a file into Events
19499              * @param {HtmlEditor} this
19500              */
19501             autosave: true,
19502             /**
19503              * @event savedpreview
19504              * preview the saved version of htmlEditor
19505              * @param {HtmlEditor} this
19506              */
19507             savedpreview: true
19508         });
19509 };
19510
19511
19512 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19513     
19514     
19515       /**
19516      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19517      */
19518     toolbars : false,
19519    
19520      /**
19521      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19522      *                        Roo.resizable.
19523      */
19524     resizable : false,
19525      /**
19526      * @cfg {Number} height (in pixels)
19527      */   
19528     height: 300,
19529    /**
19530      * @cfg {Number} width (in pixels)
19531      */   
19532     width: false,
19533     
19534     /**
19535      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19536      * 
19537      */
19538     stylesheets: false,
19539     
19540     // id of frame..
19541     frameId: false,
19542     
19543     // private properties
19544     validationEvent : false,
19545     deferHeight: true,
19546     initialized : false,
19547     activated : false,
19548     
19549     onFocus : Roo.emptyFn,
19550     iframePad:3,
19551     hideMode:'offsets',
19552     
19553     
19554     tbContainer : false,
19555     
19556     toolbarContainer :function() {
19557         return this.wrap.select('.x-html-editor-tb',true).first();
19558     },
19559
19560     /**
19561      * Protected method that will not generally be called directly. It
19562      * is called when the editor creates its toolbar. Override this method if you need to
19563      * add custom toolbar buttons.
19564      * @param {HtmlEditor} editor
19565      */
19566     createToolbar : function(){
19567         
19568         Roo.log("create toolbars");
19569         
19570         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19571         this.toolbars[0].render(this.toolbarContainer());
19572         
19573         return;
19574         
19575 //        if (!editor.toolbars || !editor.toolbars.length) {
19576 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19577 //        }
19578 //        
19579 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19580 //            editor.toolbars[i] = Roo.factory(
19581 //                    typeof(editor.toolbars[i]) == 'string' ?
19582 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19583 //                Roo.bootstrap.HtmlEditor);
19584 //            editor.toolbars[i].init(editor);
19585 //        }
19586     },
19587
19588      
19589     // private
19590     onRender : function(ct, position)
19591     {
19592        // Roo.log("Call onRender: " + this.xtype);
19593         var _t = this;
19594         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19595       
19596         this.wrap = this.inputEl().wrap({
19597             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19598         });
19599         
19600         this.editorcore.onRender(ct, position);
19601          
19602         if (this.resizable) {
19603             this.resizeEl = new Roo.Resizable(this.wrap, {
19604                 pinned : true,
19605                 wrap: true,
19606                 dynamic : true,
19607                 minHeight : this.height,
19608                 height: this.height,
19609                 handles : this.resizable,
19610                 width: this.width,
19611                 listeners : {
19612                     resize : function(r, w, h) {
19613                         _t.onResize(w,h); // -something
19614                     }
19615                 }
19616             });
19617             
19618         }
19619         this.createToolbar(this);
19620        
19621         
19622         if(!this.width && this.resizable){
19623             this.setSize(this.wrap.getSize());
19624         }
19625         if (this.resizeEl) {
19626             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19627             // should trigger onReize..
19628         }
19629         
19630     },
19631
19632     // private
19633     onResize : function(w, h)
19634     {
19635         Roo.log('resize: ' +w + ',' + h );
19636         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19637         var ew = false;
19638         var eh = false;
19639         
19640         if(this.inputEl() ){
19641             if(typeof w == 'number'){
19642                 var aw = w - this.wrap.getFrameWidth('lr');
19643                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19644                 ew = aw;
19645             }
19646             if(typeof h == 'number'){
19647                  var tbh = -11;  // fixme it needs to tool bar size!
19648                 for (var i =0; i < this.toolbars.length;i++) {
19649                     // fixme - ask toolbars for heights?
19650                     tbh += this.toolbars[i].el.getHeight();
19651                     //if (this.toolbars[i].footer) {
19652                     //    tbh += this.toolbars[i].footer.el.getHeight();
19653                     //}
19654                 }
19655               
19656                 
19657                 
19658                 
19659                 
19660                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19661                 ah -= 5; // knock a few pixes off for look..
19662                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19663                 var eh = ah;
19664             }
19665         }
19666         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19667         this.editorcore.onResize(ew,eh);
19668         
19669     },
19670
19671     /**
19672      * Toggles the editor between standard and source edit mode.
19673      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19674      */
19675     toggleSourceEdit : function(sourceEditMode)
19676     {
19677         this.editorcore.toggleSourceEdit(sourceEditMode);
19678         
19679         if(this.editorcore.sourceEditMode){
19680             Roo.log('editor - showing textarea');
19681             
19682 //            Roo.log('in');
19683 //            Roo.log(this.syncValue());
19684             this.syncValue();
19685             this.inputEl().removeClass(['hide', 'x-hidden']);
19686             this.inputEl().dom.removeAttribute('tabIndex');
19687             this.inputEl().focus();
19688         }else{
19689             Roo.log('editor - hiding textarea');
19690 //            Roo.log('out')
19691 //            Roo.log(this.pushValue()); 
19692             this.pushValue();
19693             
19694             this.inputEl().addClass(['hide', 'x-hidden']);
19695             this.inputEl().dom.setAttribute('tabIndex', -1);
19696             //this.deferFocus();
19697         }
19698          
19699         if(this.resizable){
19700             this.setSize(this.wrap.getSize());
19701         }
19702         
19703         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19704     },
19705  
19706     // private (for BoxComponent)
19707     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19708
19709     // private (for BoxComponent)
19710     getResizeEl : function(){
19711         return this.wrap;
19712     },
19713
19714     // private (for BoxComponent)
19715     getPositionEl : function(){
19716         return this.wrap;
19717     },
19718
19719     // private
19720     initEvents : function(){
19721         this.originalValue = this.getValue();
19722     },
19723
19724 //    /**
19725 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19726 //     * @method
19727 //     */
19728 //    markInvalid : Roo.emptyFn,
19729 //    /**
19730 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19731 //     * @method
19732 //     */
19733 //    clearInvalid : Roo.emptyFn,
19734
19735     setValue : function(v){
19736         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19737         this.editorcore.pushValue();
19738     },
19739
19740      
19741     // private
19742     deferFocus : function(){
19743         this.focus.defer(10, this);
19744     },
19745
19746     // doc'ed in Field
19747     focus : function(){
19748         this.editorcore.focus();
19749         
19750     },
19751       
19752
19753     // private
19754     onDestroy : function(){
19755         
19756         
19757         
19758         if(this.rendered){
19759             
19760             for (var i =0; i < this.toolbars.length;i++) {
19761                 // fixme - ask toolbars for heights?
19762                 this.toolbars[i].onDestroy();
19763             }
19764             
19765             this.wrap.dom.innerHTML = '';
19766             this.wrap.remove();
19767         }
19768     },
19769
19770     // private
19771     onFirstFocus : function(){
19772         //Roo.log("onFirstFocus");
19773         this.editorcore.onFirstFocus();
19774          for (var i =0; i < this.toolbars.length;i++) {
19775             this.toolbars[i].onFirstFocus();
19776         }
19777         
19778     },
19779     
19780     // private
19781     syncValue : function()
19782     {   
19783         this.editorcore.syncValue();
19784     },
19785     
19786     pushValue : function()
19787     {   
19788         this.editorcore.pushValue();
19789     }
19790      
19791     
19792     // hide stuff that is not compatible
19793     /**
19794      * @event blur
19795      * @hide
19796      */
19797     /**
19798      * @event change
19799      * @hide
19800      */
19801     /**
19802      * @event focus
19803      * @hide
19804      */
19805     /**
19806      * @event specialkey
19807      * @hide
19808      */
19809     /**
19810      * @cfg {String} fieldClass @hide
19811      */
19812     /**
19813      * @cfg {String} focusClass @hide
19814      */
19815     /**
19816      * @cfg {String} autoCreate @hide
19817      */
19818     /**
19819      * @cfg {String} inputType @hide
19820      */
19821     /**
19822      * @cfg {String} invalidClass @hide
19823      */
19824     /**
19825      * @cfg {String} invalidText @hide
19826      */
19827     /**
19828      * @cfg {String} msgFx @hide
19829      */
19830     /**
19831      * @cfg {String} validateOnBlur @hide
19832      */
19833 });
19834  
19835     
19836    
19837    
19838    
19839       
19840 Roo.namespace('Roo.bootstrap.htmleditor');
19841 /**
19842  * @class Roo.bootstrap.HtmlEditorToolbar1
19843  * Basic Toolbar
19844  * 
19845  * Usage:
19846  *
19847  new Roo.bootstrap.HtmlEditor({
19848     ....
19849     toolbars : [
19850         new Roo.bootstrap.HtmlEditorToolbar1({
19851             disable : { fonts: 1 , format: 1, ..., ... , ...],
19852             btns : [ .... ]
19853         })
19854     }
19855      
19856  * 
19857  * @cfg {Object} disable List of elements to disable..
19858  * @cfg {Array} btns List of additional buttons.
19859  * 
19860  * 
19861  * NEEDS Extra CSS? 
19862  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19863  */
19864  
19865 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19866 {
19867     
19868     Roo.apply(this, config);
19869     
19870     // default disabled, based on 'good practice'..
19871     this.disable = this.disable || {};
19872     Roo.applyIf(this.disable, {
19873         fontSize : true,
19874         colors : true,
19875         specialElements : true
19876     });
19877     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19878     
19879     this.editor = config.editor;
19880     this.editorcore = config.editor.editorcore;
19881     
19882     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19883     
19884     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19885     // dont call parent... till later.
19886 }
19887 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19888      
19889     bar : true,
19890     
19891     editor : false,
19892     editorcore : false,
19893     
19894     
19895     formats : [
19896         "p" ,  
19897         "h1","h2","h3","h4","h5","h6", 
19898         "pre", "code", 
19899         "abbr", "acronym", "address", "cite", "samp", "var",
19900         'div','span'
19901     ],
19902     
19903     onRender : function(ct, position)
19904     {
19905        // Roo.log("Call onRender: " + this.xtype);
19906         
19907        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19908        Roo.log(this.el);
19909        this.el.dom.style.marginBottom = '0';
19910        var _this = this;
19911        var editorcore = this.editorcore;
19912        var editor= this.editor;
19913        
19914        var children = [];
19915        var btn = function(id,cmd , toggle, handler){
19916        
19917             var  event = toggle ? 'toggle' : 'click';
19918        
19919             var a = {
19920                 size : 'sm',
19921                 xtype: 'Button',
19922                 xns: Roo.bootstrap,
19923                 glyphicon : id,
19924                 cmd : id || cmd,
19925                 enableToggle:toggle !== false,
19926                 //html : 'submit'
19927                 pressed : toggle ? false : null,
19928                 listeners : {}
19929             }
19930             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19931                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19932             }
19933             children.push(a);
19934             return a;
19935        }
19936         
19937         var style = {
19938                 xtype: 'Button',
19939                 size : 'sm',
19940                 xns: Roo.bootstrap,
19941                 glyphicon : 'font',
19942                 //html : 'submit'
19943                 menu : {
19944                     xtype: 'Menu',
19945                     xns: Roo.bootstrap,
19946                     items:  []
19947                 }
19948         };
19949         Roo.each(this.formats, function(f) {
19950             style.menu.items.push({
19951                 xtype :'MenuItem',
19952                 xns: Roo.bootstrap,
19953                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19954                 tagname : f,
19955                 listeners : {
19956                     click : function()
19957                     {
19958                         editorcore.insertTag(this.tagname);
19959                         editor.focus();
19960                     }
19961                 }
19962                 
19963             });
19964         });
19965          children.push(style);   
19966             
19967             
19968         btn('bold',false,true);
19969         btn('italic',false,true);
19970         btn('align-left', 'justifyleft',true);
19971         btn('align-center', 'justifycenter',true);
19972         btn('align-right' , 'justifyright',true);
19973         btn('link', false, false, function(btn) {
19974             //Roo.log("create link?");
19975             var url = prompt(this.createLinkText, this.defaultLinkValue);
19976             if(url && url != 'http:/'+'/'){
19977                 this.editorcore.relayCmd('createlink', url);
19978             }
19979         }),
19980         btn('list','insertunorderedlist',true);
19981         btn('pencil', false,true, function(btn){
19982                 Roo.log(this);
19983                 
19984                 this.toggleSourceEdit(btn.pressed);
19985         });
19986         /*
19987         var cog = {
19988                 xtype: 'Button',
19989                 size : 'sm',
19990                 xns: Roo.bootstrap,
19991                 glyphicon : 'cog',
19992                 //html : 'submit'
19993                 menu : {
19994                     xtype: 'Menu',
19995                     xns: Roo.bootstrap,
19996                     items:  []
19997                 }
19998         };
19999         
20000         cog.menu.items.push({
20001             xtype :'MenuItem',
20002             xns: Roo.bootstrap,
20003             html : Clean styles,
20004             tagname : f,
20005             listeners : {
20006                 click : function()
20007                 {
20008                     editorcore.insertTag(this.tagname);
20009                     editor.focus();
20010                 }
20011             }
20012             
20013         });
20014        */
20015         
20016          
20017        this.xtype = 'NavSimplebar';
20018         
20019         for(var i=0;i< children.length;i++) {
20020             
20021             this.buttons.add(this.addxtypeChild(children[i]));
20022             
20023         }
20024         
20025         editor.on('editorevent', this.updateToolbar, this);
20026     },
20027     onBtnClick : function(id)
20028     {
20029        this.editorcore.relayCmd(id);
20030        this.editorcore.focus();
20031     },
20032     
20033     /**
20034      * Protected method that will not generally be called directly. It triggers
20035      * a toolbar update by reading the markup state of the current selection in the editor.
20036      */
20037     updateToolbar: function(){
20038
20039         if(!this.editorcore.activated){
20040             this.editor.onFirstFocus(); // is this neeed?
20041             return;
20042         }
20043
20044         var btns = this.buttons; 
20045         var doc = this.editorcore.doc;
20046         btns.get('bold').setActive(doc.queryCommandState('bold'));
20047         btns.get('italic').setActive(doc.queryCommandState('italic'));
20048         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20049         
20050         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20051         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20052         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20053         
20054         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20055         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20056          /*
20057         
20058         var ans = this.editorcore.getAllAncestors();
20059         if (this.formatCombo) {
20060             
20061             
20062             var store = this.formatCombo.store;
20063             this.formatCombo.setValue("");
20064             for (var i =0; i < ans.length;i++) {
20065                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20066                     // select it..
20067                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20068                     break;
20069                 }
20070             }
20071         }
20072         
20073         
20074         
20075         // hides menus... - so this cant be on a menu...
20076         Roo.bootstrap.MenuMgr.hideAll();
20077         */
20078         Roo.bootstrap.MenuMgr.hideAll();
20079         //this.editorsyncValue();
20080     },
20081     onFirstFocus: function() {
20082         this.buttons.each(function(item){
20083            item.enable();
20084         });
20085     },
20086     toggleSourceEdit : function(sourceEditMode){
20087         
20088           
20089         if(sourceEditMode){
20090             Roo.log("disabling buttons");
20091            this.buttons.each( function(item){
20092                 if(item.cmd != 'pencil'){
20093                     item.disable();
20094                 }
20095             });
20096           
20097         }else{
20098             Roo.log("enabling buttons");
20099             if(this.editorcore.initialized){
20100                 this.buttons.each( function(item){
20101                     item.enable();
20102                 });
20103             }
20104             
20105         }
20106         Roo.log("calling toggole on editor");
20107         // tell the editor that it's been pressed..
20108         this.editor.toggleSourceEdit(sourceEditMode);
20109        
20110     }
20111 });
20112
20113
20114
20115
20116
20117 /**
20118  * @class Roo.bootstrap.Table.AbstractSelectionModel
20119  * @extends Roo.util.Observable
20120  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20121  * implemented by descendant classes.  This class should not be directly instantiated.
20122  * @constructor
20123  */
20124 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20125     this.locked = false;
20126     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20127 };
20128
20129
20130 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20131     /** @ignore Called by the grid automatically. Do not call directly. */
20132     init : function(grid){
20133         this.grid = grid;
20134         this.initEvents();
20135     },
20136
20137     /**
20138      * Locks the selections.
20139      */
20140     lock : function(){
20141         this.locked = true;
20142     },
20143
20144     /**
20145      * Unlocks the selections.
20146      */
20147     unlock : function(){
20148         this.locked = false;
20149     },
20150
20151     /**
20152      * Returns true if the selections are locked.
20153      * @return {Boolean}
20154      */
20155     isLocked : function(){
20156         return this.locked;
20157     }
20158 });
20159 /**
20160  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20161  * @class Roo.bootstrap.Table.RowSelectionModel
20162  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20163  * It supports multiple selections and keyboard selection/navigation. 
20164  * @constructor
20165  * @param {Object} config
20166  */
20167
20168 Roo.bootstrap.Table.RowSelectionModel = function(config){
20169     Roo.apply(this, config);
20170     this.selections = new Roo.util.MixedCollection(false, function(o){
20171         return o.id;
20172     });
20173
20174     this.last = false;
20175     this.lastActive = false;
20176
20177     this.addEvents({
20178         /**
20179              * @event selectionchange
20180              * Fires when the selection changes
20181              * @param {SelectionModel} this
20182              */
20183             "selectionchange" : true,
20184         /**
20185              * @event afterselectionchange
20186              * Fires after the selection changes (eg. by key press or clicking)
20187              * @param {SelectionModel} this
20188              */
20189             "afterselectionchange" : true,
20190         /**
20191              * @event beforerowselect
20192              * Fires when a row is selected being selected, return false to cancel.
20193              * @param {SelectionModel} this
20194              * @param {Number} rowIndex The selected index
20195              * @param {Boolean} keepExisting False if other selections will be cleared
20196              */
20197             "beforerowselect" : true,
20198         /**
20199              * @event rowselect
20200              * Fires when a row is selected.
20201              * @param {SelectionModel} this
20202              * @param {Number} rowIndex The selected index
20203              * @param {Roo.data.Record} r The record
20204              */
20205             "rowselect" : true,
20206         /**
20207              * @event rowdeselect
20208              * Fires when a row is deselected.
20209              * @param {SelectionModel} this
20210              * @param {Number} rowIndex The selected index
20211              */
20212         "rowdeselect" : true
20213     });
20214     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20215     this.locked = false;
20216 };
20217
20218 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20219     /**
20220      * @cfg {Boolean} singleSelect
20221      * True to allow selection of only one row at a time (defaults to false)
20222      */
20223     singleSelect : false,
20224
20225     // private
20226     initEvents : function(){
20227
20228         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20229             this.grid.on("mousedown", this.handleMouseDown, this);
20230         }else{ // allow click to work like normal
20231             this.grid.on("rowclick", this.handleDragableRowClick, this);
20232         }
20233
20234         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20235             "up" : function(e){
20236                 if(!e.shiftKey){
20237                     this.selectPrevious(e.shiftKey);
20238                 }else if(this.last !== false && this.lastActive !== false){
20239                     var last = this.last;
20240                     this.selectRange(this.last,  this.lastActive-1);
20241                     this.grid.getView().focusRow(this.lastActive);
20242                     if(last !== false){
20243                         this.last = last;
20244                     }
20245                 }else{
20246                     this.selectFirstRow();
20247                 }
20248                 this.fireEvent("afterselectionchange", this);
20249             },
20250             "down" : function(e){
20251                 if(!e.shiftKey){
20252                     this.selectNext(e.shiftKey);
20253                 }else if(this.last !== false && this.lastActive !== false){
20254                     var last = this.last;
20255                     this.selectRange(this.last,  this.lastActive+1);
20256                     this.grid.getView().focusRow(this.lastActive);
20257                     if(last !== false){
20258                         this.last = last;
20259                     }
20260                 }else{
20261                     this.selectFirstRow();
20262                 }
20263                 this.fireEvent("afterselectionchange", this);
20264             },
20265             scope: this
20266         });
20267
20268         var view = this.grid.view;
20269         view.on("refresh", this.onRefresh, this);
20270         view.on("rowupdated", this.onRowUpdated, this);
20271         view.on("rowremoved", this.onRemove, this);
20272     },
20273
20274     // private
20275     onRefresh : function(){
20276         var ds = this.grid.dataSource, i, v = this.grid.view;
20277         var s = this.selections;
20278         s.each(function(r){
20279             if((i = ds.indexOfId(r.id)) != -1){
20280                 v.onRowSelect(i);
20281             }else{
20282                 s.remove(r);
20283             }
20284         });
20285     },
20286
20287     // private
20288     onRemove : function(v, index, r){
20289         this.selections.remove(r);
20290     },
20291
20292     // private
20293     onRowUpdated : function(v, index, r){
20294         if(this.isSelected(r)){
20295             v.onRowSelect(index);
20296         }
20297     },
20298
20299     /**
20300      * Select records.
20301      * @param {Array} records The records to select
20302      * @param {Boolean} keepExisting (optional) True to keep existing selections
20303      */
20304     selectRecords : function(records, keepExisting){
20305         if(!keepExisting){
20306             this.clearSelections();
20307         }
20308         var ds = this.grid.dataSource;
20309         for(var i = 0, len = records.length; i < len; i++){
20310             this.selectRow(ds.indexOf(records[i]), true);
20311         }
20312     },
20313
20314     /**
20315      * Gets the number of selected rows.
20316      * @return {Number}
20317      */
20318     getCount : function(){
20319         return this.selections.length;
20320     },
20321
20322     /**
20323      * Selects the first row in the grid.
20324      */
20325     selectFirstRow : function(){
20326         this.selectRow(0);
20327     },
20328
20329     /**
20330      * Select the last row.
20331      * @param {Boolean} keepExisting (optional) True to keep existing selections
20332      */
20333     selectLastRow : function(keepExisting){
20334         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20335     },
20336
20337     /**
20338      * Selects the row immediately following the last selected row.
20339      * @param {Boolean} keepExisting (optional) True to keep existing selections
20340      */
20341     selectNext : function(keepExisting){
20342         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20343             this.selectRow(this.last+1, keepExisting);
20344             this.grid.getView().focusRow(this.last);
20345         }
20346     },
20347
20348     /**
20349      * Selects the row that precedes the last selected row.
20350      * @param {Boolean} keepExisting (optional) True to keep existing selections
20351      */
20352     selectPrevious : function(keepExisting){
20353         if(this.last){
20354             this.selectRow(this.last-1, keepExisting);
20355             this.grid.getView().focusRow(this.last);
20356         }
20357     },
20358
20359     /**
20360      * Returns the selected records
20361      * @return {Array} Array of selected records
20362      */
20363     getSelections : function(){
20364         return [].concat(this.selections.items);
20365     },
20366
20367     /**
20368      * Returns the first selected record.
20369      * @return {Record}
20370      */
20371     getSelected : function(){
20372         return this.selections.itemAt(0);
20373     },
20374
20375
20376     /**
20377      * Clears all selections.
20378      */
20379     clearSelections : function(fast){
20380         if(this.locked) return;
20381         if(fast !== true){
20382             var ds = this.grid.dataSource;
20383             var s = this.selections;
20384             s.each(function(r){
20385                 this.deselectRow(ds.indexOfId(r.id));
20386             }, this);
20387             s.clear();
20388         }else{
20389             this.selections.clear();
20390         }
20391         this.last = false;
20392     },
20393
20394
20395     /**
20396      * Selects all rows.
20397      */
20398     selectAll : function(){
20399         if(this.locked) return;
20400         this.selections.clear();
20401         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20402             this.selectRow(i, true);
20403         }
20404     },
20405
20406     /**
20407      * Returns True if there is a selection.
20408      * @return {Boolean}
20409      */
20410     hasSelection : function(){
20411         return this.selections.length > 0;
20412     },
20413
20414     /**
20415      * Returns True if the specified row is selected.
20416      * @param {Number/Record} record The record or index of the record to check
20417      * @return {Boolean}
20418      */
20419     isSelected : function(index){
20420         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20421         return (r && this.selections.key(r.id) ? true : false);
20422     },
20423
20424     /**
20425      * Returns True if the specified record id is selected.
20426      * @param {String} id The id of record to check
20427      * @return {Boolean}
20428      */
20429     isIdSelected : function(id){
20430         return (this.selections.key(id) ? true : false);
20431     },
20432
20433     // private
20434     handleMouseDown : function(e, t){
20435         var view = this.grid.getView(), rowIndex;
20436         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20437             return;
20438         };
20439         if(e.shiftKey && this.last !== false){
20440             var last = this.last;
20441             this.selectRange(last, rowIndex, e.ctrlKey);
20442             this.last = last; // reset the last
20443             view.focusRow(rowIndex);
20444         }else{
20445             var isSelected = this.isSelected(rowIndex);
20446             if(e.button !== 0 && isSelected){
20447                 view.focusRow(rowIndex);
20448             }else if(e.ctrlKey && isSelected){
20449                 this.deselectRow(rowIndex);
20450             }else if(!isSelected){
20451                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20452                 view.focusRow(rowIndex);
20453             }
20454         }
20455         this.fireEvent("afterselectionchange", this);
20456     },
20457     // private
20458     handleDragableRowClick :  function(grid, rowIndex, e) 
20459     {
20460         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20461             this.selectRow(rowIndex, false);
20462             grid.view.focusRow(rowIndex);
20463              this.fireEvent("afterselectionchange", this);
20464         }
20465     },
20466     
20467     /**
20468      * Selects multiple rows.
20469      * @param {Array} rows Array of the indexes of the row to select
20470      * @param {Boolean} keepExisting (optional) True to keep existing selections
20471      */
20472     selectRows : function(rows, keepExisting){
20473         if(!keepExisting){
20474             this.clearSelections();
20475         }
20476         for(var i = 0, len = rows.length; i < len; i++){
20477             this.selectRow(rows[i], true);
20478         }
20479     },
20480
20481     /**
20482      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20483      * @param {Number} startRow The index of the first row in the range
20484      * @param {Number} endRow The index of the last row in the range
20485      * @param {Boolean} keepExisting (optional) True to retain existing selections
20486      */
20487     selectRange : function(startRow, endRow, keepExisting){
20488         if(this.locked) return;
20489         if(!keepExisting){
20490             this.clearSelections();
20491         }
20492         if(startRow <= endRow){
20493             for(var i = startRow; i <= endRow; i++){
20494                 this.selectRow(i, true);
20495             }
20496         }else{
20497             for(var i = startRow; i >= endRow; i--){
20498                 this.selectRow(i, true);
20499             }
20500         }
20501     },
20502
20503     /**
20504      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20505      * @param {Number} startRow The index of the first row in the range
20506      * @param {Number} endRow The index of the last row in the range
20507      */
20508     deselectRange : function(startRow, endRow, preventViewNotify){
20509         if(this.locked) return;
20510         for(var i = startRow; i <= endRow; i++){
20511             this.deselectRow(i, preventViewNotify);
20512         }
20513     },
20514
20515     /**
20516      * Selects a row.
20517      * @param {Number} row The index of the row to select
20518      * @param {Boolean} keepExisting (optional) True to keep existing selections
20519      */
20520     selectRow : function(index, keepExisting, preventViewNotify){
20521         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20522         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20523             if(!keepExisting || this.singleSelect){
20524                 this.clearSelections();
20525             }
20526             var r = this.grid.dataSource.getAt(index);
20527             this.selections.add(r);
20528             this.last = this.lastActive = index;
20529             if(!preventViewNotify){
20530                 this.grid.getView().onRowSelect(index);
20531             }
20532             this.fireEvent("rowselect", this, index, r);
20533             this.fireEvent("selectionchange", this);
20534         }
20535     },
20536
20537     /**
20538      * Deselects a row.
20539      * @param {Number} row The index of the row to deselect
20540      */
20541     deselectRow : function(index, preventViewNotify){
20542         if(this.locked) return;
20543         if(this.last == index){
20544             this.last = false;
20545         }
20546         if(this.lastActive == index){
20547             this.lastActive = false;
20548         }
20549         var r = this.grid.dataSource.getAt(index);
20550         this.selections.remove(r);
20551         if(!preventViewNotify){
20552             this.grid.getView().onRowDeselect(index);
20553         }
20554         this.fireEvent("rowdeselect", this, index);
20555         this.fireEvent("selectionchange", this);
20556     },
20557
20558     // private
20559     restoreLast : function(){
20560         if(this._last){
20561             this.last = this._last;
20562         }
20563     },
20564
20565     // private
20566     acceptsNav : function(row, col, cm){
20567         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20568     },
20569
20570     // private
20571     onEditorKey : function(field, e){
20572         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20573         if(k == e.TAB){
20574             e.stopEvent();
20575             ed.completeEdit();
20576             if(e.shiftKey){
20577                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20578             }else{
20579                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20580             }
20581         }else if(k == e.ENTER && !e.ctrlKey){
20582             e.stopEvent();
20583             ed.completeEdit();
20584             if(e.shiftKey){
20585                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20586             }else{
20587                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20588             }
20589         }else if(k == e.ESC){
20590             ed.cancelEdit();
20591         }
20592         if(newCell){
20593             g.startEditing(newCell[0], newCell[1]);
20594         }
20595     }
20596 });/*
20597  * Based on:
20598  * Ext JS Library 1.1.1
20599  * Copyright(c) 2006-2007, Ext JS, LLC.
20600  *
20601  * Originally Released Under LGPL - original licence link has changed is not relivant.
20602  *
20603  * Fork - LGPL
20604  * <script type="text/javascript">
20605  */
20606  
20607 /**
20608  * @class Roo.bootstrap.PagingToolbar
20609  * @extends Roo.Row
20610  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20611  * @constructor
20612  * Create a new PagingToolbar
20613  * @param {Object} config The config object
20614  */
20615 Roo.bootstrap.PagingToolbar = function(config)
20616 {
20617     // old args format still supported... - xtype is prefered..
20618         // created from xtype...
20619     var ds = config.dataSource;
20620     this.toolbarItems = [];
20621     if (config.items) {
20622         this.toolbarItems = config.items;
20623 //        config.items = [];
20624     }
20625     
20626     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20627     this.ds = ds;
20628     this.cursor = 0;
20629     if (ds) { 
20630         this.bind(ds);
20631     }
20632     
20633     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20634     
20635 };
20636
20637 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20638     /**
20639      * @cfg {Roo.data.Store} dataSource
20640      * The underlying data store providing the paged data
20641      */
20642     /**
20643      * @cfg {String/HTMLElement/Element} container
20644      * container The id or element that will contain the toolbar
20645      */
20646     /**
20647      * @cfg {Boolean} displayInfo
20648      * True to display the displayMsg (defaults to false)
20649      */
20650     /**
20651      * @cfg {Number} pageSize
20652      * The number of records to display per page (defaults to 20)
20653      */
20654     pageSize: 20,
20655     /**
20656      * @cfg {String} displayMsg
20657      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20658      */
20659     displayMsg : 'Displaying {0} - {1} of {2}',
20660     /**
20661      * @cfg {String} emptyMsg
20662      * The message to display when no records are found (defaults to "No data to display")
20663      */
20664     emptyMsg : 'No data to display',
20665     /**
20666      * Customizable piece of the default paging text (defaults to "Page")
20667      * @type String
20668      */
20669     beforePageText : "Page",
20670     /**
20671      * Customizable piece of the default paging text (defaults to "of %0")
20672      * @type String
20673      */
20674     afterPageText : "of {0}",
20675     /**
20676      * Customizable piece of the default paging text (defaults to "First Page")
20677      * @type String
20678      */
20679     firstText : "First Page",
20680     /**
20681      * Customizable piece of the default paging text (defaults to "Previous Page")
20682      * @type String
20683      */
20684     prevText : "Previous Page",
20685     /**
20686      * Customizable piece of the default paging text (defaults to "Next Page")
20687      * @type String
20688      */
20689     nextText : "Next Page",
20690     /**
20691      * Customizable piece of the default paging text (defaults to "Last Page")
20692      * @type String
20693      */
20694     lastText : "Last Page",
20695     /**
20696      * Customizable piece of the default paging text (defaults to "Refresh")
20697      * @type String
20698      */
20699     refreshText : "Refresh",
20700
20701     buttons : false,
20702     // private
20703     onRender : function(ct, position) 
20704     {
20705         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20706         this.navgroup.parentId = this.id;
20707         this.navgroup.onRender(this.el, null);
20708         // add the buttons to the navgroup
20709         
20710         if(this.displayInfo){
20711             Roo.log(this.el.select('ul.navbar-nav',true).first());
20712             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20713             this.displayEl = this.el.select('.x-paging-info', true).first();
20714 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20715 //            this.displayEl = navel.el.select('span',true).first();
20716         }
20717         
20718         var _this = this;
20719         
20720         if(this.buttons){
20721             Roo.each(_this.buttons, function(e){
20722                Roo.factory(e).onRender(_this.el, null);
20723             });
20724         }
20725             
20726         Roo.each(_this.toolbarItems, function(e) {
20727             _this.navgroup.addItem(e);
20728         });
20729         
20730         
20731         this.first = this.navgroup.addItem({
20732             tooltip: this.firstText,
20733             cls: "prev",
20734             icon : 'fa fa-backward',
20735             disabled: true,
20736             preventDefault: true,
20737             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20738         });
20739         
20740         this.prev =  this.navgroup.addItem({
20741             tooltip: this.prevText,
20742             cls: "prev",
20743             icon : 'fa fa-step-backward',
20744             disabled: true,
20745             preventDefault: true,
20746             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20747         });
20748     //this.addSeparator();
20749         
20750         
20751         var field = this.navgroup.addItem( {
20752             tagtype : 'span',
20753             cls : 'x-paging-position',
20754             
20755             html : this.beforePageText  +
20756                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20757                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20758          } ); //?? escaped?
20759         
20760         this.field = field.el.select('input', true).first();
20761         this.field.on("keydown", this.onPagingKeydown, this);
20762         this.field.on("focus", function(){this.dom.select();});
20763     
20764     
20765         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20766         //this.field.setHeight(18);
20767         //this.addSeparator();
20768         this.next = this.navgroup.addItem({
20769             tooltip: this.nextText,
20770             cls: "next",
20771             html : ' <i class="fa fa-step-forward">',
20772             disabled: true,
20773             preventDefault: true,
20774             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20775         });
20776         this.last = this.navgroup.addItem({
20777             tooltip: this.lastText,
20778             icon : 'fa fa-forward',
20779             cls: "next",
20780             disabled: true,
20781             preventDefault: true,
20782             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20783         });
20784     //this.addSeparator();
20785         this.loading = this.navgroup.addItem({
20786             tooltip: this.refreshText,
20787             icon: 'fa fa-refresh',
20788             preventDefault: true,
20789             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20790         });
20791
20792     },
20793
20794     // private
20795     updateInfo : function(){
20796         if(this.displayEl){
20797             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20798             var msg = count == 0 ?
20799                 this.emptyMsg :
20800                 String.format(
20801                     this.displayMsg,
20802                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20803                 );
20804             this.displayEl.update(msg);
20805         }
20806     },
20807
20808     // private
20809     onLoad : function(ds, r, o){
20810        this.cursor = o.params ? o.params.start : 0;
20811        var d = this.getPageData(),
20812             ap = d.activePage,
20813             ps = d.pages;
20814         
20815        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20816        this.field.dom.value = ap;
20817        this.first.setDisabled(ap == 1);
20818        this.prev.setDisabled(ap == 1);
20819        this.next.setDisabled(ap == ps);
20820        this.last.setDisabled(ap == ps);
20821        this.loading.enable();
20822        this.updateInfo();
20823     },
20824
20825     // private
20826     getPageData : function(){
20827         var total = this.ds.getTotalCount();
20828         return {
20829             total : total,
20830             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20831             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20832         };
20833     },
20834
20835     // private
20836     onLoadError : function(){
20837         this.loading.enable();
20838     },
20839
20840     // private
20841     onPagingKeydown : function(e){
20842         var k = e.getKey();
20843         var d = this.getPageData();
20844         if(k == e.RETURN){
20845             var v = this.field.dom.value, pageNum;
20846             if(!v || isNaN(pageNum = parseInt(v, 10))){
20847                 this.field.dom.value = d.activePage;
20848                 return;
20849             }
20850             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20851             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20852             e.stopEvent();
20853         }
20854         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))
20855         {
20856           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20857           this.field.dom.value = pageNum;
20858           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20859           e.stopEvent();
20860         }
20861         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20862         {
20863           var v = this.field.dom.value, pageNum; 
20864           var increment = (e.shiftKey) ? 10 : 1;
20865           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20866             increment *= -1;
20867           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20868             this.field.dom.value = d.activePage;
20869             return;
20870           }
20871           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20872           {
20873             this.field.dom.value = parseInt(v, 10) + increment;
20874             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20875             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20876           }
20877           e.stopEvent();
20878         }
20879     },
20880
20881     // private
20882     beforeLoad : function(){
20883         if(this.loading){
20884             this.loading.disable();
20885         }
20886     },
20887
20888     // private
20889     onClick : function(which){
20890         
20891         var ds = this.ds;
20892         if (!ds) {
20893             return;
20894         }
20895         
20896         switch(which){
20897             case "first":
20898                 ds.load({params:{start: 0, limit: this.pageSize}});
20899             break;
20900             case "prev":
20901                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20902             break;
20903             case "next":
20904                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20905             break;
20906             case "last":
20907                 var total = ds.getTotalCount();
20908                 var extra = total % this.pageSize;
20909                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20910                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20911             break;
20912             case "refresh":
20913                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20914             break;
20915         }
20916     },
20917
20918     /**
20919      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20920      * @param {Roo.data.Store} store The data store to unbind
20921      */
20922     unbind : function(ds){
20923         ds.un("beforeload", this.beforeLoad, this);
20924         ds.un("load", this.onLoad, this);
20925         ds.un("loadexception", this.onLoadError, this);
20926         ds.un("remove", this.updateInfo, this);
20927         ds.un("add", this.updateInfo, this);
20928         this.ds = undefined;
20929     },
20930
20931     /**
20932      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20933      * @param {Roo.data.Store} store The data store to bind
20934      */
20935     bind : function(ds){
20936         ds.on("beforeload", this.beforeLoad, this);
20937         ds.on("load", this.onLoad, this);
20938         ds.on("loadexception", this.onLoadError, this);
20939         ds.on("remove", this.updateInfo, this);
20940         ds.on("add", this.updateInfo, this);
20941         this.ds = ds;
20942     }
20943 });/*
20944  * - LGPL
20945  *
20946  * element
20947  * 
20948  */
20949
20950 /**
20951  * @class Roo.bootstrap.MessageBar
20952  * @extends Roo.bootstrap.Component
20953  * Bootstrap MessageBar class
20954  * @cfg {String} html contents of the MessageBar
20955  * @cfg {String} weight (info | success | warning | danger) default info
20956  * @cfg {String} beforeClass insert the bar before the given class
20957  * @cfg {Boolean} closable (true | false) default false
20958  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20959  * 
20960  * @constructor
20961  * Create a new Element
20962  * @param {Object} config The config object
20963  */
20964
20965 Roo.bootstrap.MessageBar = function(config){
20966     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20967 };
20968
20969 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20970     
20971     html: '',
20972     weight: 'info',
20973     closable: false,
20974     fixed: false,
20975     beforeClass: 'bootstrap-sticky-wrap',
20976     
20977     getAutoCreate : function(){
20978         
20979         var cfg = {
20980             tag: 'div',
20981             cls: 'alert alert-dismissable alert-' + this.weight,
20982             cn: [
20983                 {
20984                     tag: 'span',
20985                     cls: 'message',
20986                     html: this.html || ''
20987                 }
20988             ]
20989         }
20990         
20991         if(this.fixed){
20992             cfg.cls += ' alert-messages-fixed';
20993         }
20994         
20995         if(this.closable){
20996             cfg.cn.push({
20997                 tag: 'button',
20998                 cls: 'close',
20999                 html: 'x'
21000             });
21001         }
21002         
21003         return cfg;
21004     },
21005     
21006     onRender : function(ct, position)
21007     {
21008         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21009         
21010         if(!this.el){
21011             var cfg = Roo.apply({},  this.getAutoCreate());
21012             cfg.id = Roo.id();
21013             
21014             if (this.cls) {
21015                 cfg.cls += ' ' + this.cls;
21016             }
21017             if (this.style) {
21018                 cfg.style = this.style;
21019             }
21020             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21021             
21022             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21023         }
21024         
21025         this.el.select('>button.close').on('click', this.hide, this);
21026         
21027     },
21028     
21029     show : function()
21030     {
21031         if (!this.rendered) {
21032             this.render();
21033         }
21034         
21035         this.el.show();
21036         
21037         this.fireEvent('show', this);
21038         
21039     },
21040     
21041     hide : function()
21042     {
21043         if (!this.rendered) {
21044             this.render();
21045         }
21046         
21047         this.el.hide();
21048         
21049         this.fireEvent('hide', this);
21050     },
21051     
21052     update : function()
21053     {
21054 //        var e = this.el.dom.firstChild;
21055 //        
21056 //        if(this.closable){
21057 //            e = e.nextSibling;
21058 //        }
21059 //        
21060 //        e.data = this.html || '';
21061
21062         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21063     }
21064    
21065 });
21066
21067  
21068
21069      /*
21070  * - LGPL
21071  *
21072  * Graph
21073  * 
21074  */
21075
21076
21077 /**
21078  * @class Roo.bootstrap.Graph
21079  * @extends Roo.bootstrap.Component
21080  * Bootstrap Graph class
21081 > Prameters
21082  -sm {number} sm 4
21083  -md {number} md 5
21084  @cfg {String} graphtype  bar | vbar | pie
21085  @cfg {number} g_x coodinator | centre x (pie)
21086  @cfg {number} g_y coodinator | centre y (pie)
21087  @cfg {number} g_r radius (pie)
21088  @cfg {number} g_height height of the chart (respected by all elements in the set)
21089  @cfg {number} g_width width of the chart (respected by all elements in the set)
21090  @cfg {Object} title The title of the chart
21091     
21092  -{Array}  values
21093  -opts (object) options for the chart 
21094      o {
21095      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21096      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21097      o vgutter (number)
21098      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.
21099      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21100      o to
21101      o stretch (boolean)
21102      o }
21103  -opts (object) options for the pie
21104      o{
21105      o cut
21106      o startAngle (number)
21107      o endAngle (number)
21108      } 
21109  *
21110  * @constructor
21111  * Create a new Input
21112  * @param {Object} config The config object
21113  */
21114
21115 Roo.bootstrap.Graph = function(config){
21116     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21117     
21118     this.addEvents({
21119         // img events
21120         /**
21121          * @event click
21122          * The img click event for the img.
21123          * @param {Roo.EventObject} e
21124          */
21125         "click" : true
21126     });
21127 };
21128
21129 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21130     
21131     sm: 4,
21132     md: 5,
21133     graphtype: 'bar',
21134     g_height: 250,
21135     g_width: 400,
21136     g_x: 50,
21137     g_y: 50,
21138     g_r: 30,
21139     opts:{
21140         //g_colors: this.colors,
21141         g_type: 'soft',
21142         g_gutter: '20%'
21143
21144     },
21145     title : false,
21146
21147     getAutoCreate : function(){
21148         
21149         var cfg = {
21150             tag: 'div',
21151             html : null
21152         }
21153         
21154         
21155         return  cfg;
21156     },
21157
21158     onRender : function(ct,position){
21159         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21160         this.raphael = Raphael(this.el.dom);
21161         
21162                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21163                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21164                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21165                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21166                 /*
21167                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21168                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21169                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21170                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21171                 
21172                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21173                 r.barchart(330, 10, 300, 220, data1);
21174                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21175                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21176                 */
21177                 
21178                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21179                 // r.barchart(30, 30, 560, 250,  xdata, {
21180                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21181                 //     axis : "0 0 1 1",
21182                 //     axisxlabels :  xdata
21183                 //     //yvalues : cols,
21184                    
21185                 // });
21186 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21187 //        
21188 //        this.load(null,xdata,{
21189 //                axis : "0 0 1 1",
21190 //                axisxlabels :  xdata
21191 //                });
21192
21193     },
21194
21195     load : function(graphtype,xdata,opts){
21196         this.raphael.clear();
21197         if(!graphtype) {
21198             graphtype = this.graphtype;
21199         }
21200         if(!opts){
21201             opts = this.opts;
21202         }
21203         var r = this.raphael,
21204             fin = function () {
21205                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21206             },
21207             fout = function () {
21208                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21209             },
21210             pfin = function() {
21211                 this.sector.stop();
21212                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21213
21214                 if (this.label) {
21215                     this.label[0].stop();
21216                     this.label[0].attr({ r: 7.5 });
21217                     this.label[1].attr({ "font-weight": 800 });
21218                 }
21219             },
21220             pfout = function() {
21221                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21222
21223                 if (this.label) {
21224                     this.label[0].animate({ r: 5 }, 500, "bounce");
21225                     this.label[1].attr({ "font-weight": 400 });
21226                 }
21227             };
21228
21229         switch(graphtype){
21230             case 'bar':
21231                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21232                 break;
21233             case 'hbar':
21234                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21235                 break;
21236             case 'pie':
21237 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21238 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21239 //            
21240                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21241                 
21242                 break;
21243
21244         }
21245         
21246         if(this.title){
21247             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21248         }
21249         
21250     },
21251     
21252     setTitle: function(o)
21253     {
21254         this.title = o;
21255     },
21256     
21257     initEvents: function() {
21258         
21259         if(!this.href){
21260             this.el.on('click', this.onClick, this);
21261         }
21262     },
21263     
21264     onClick : function(e)
21265     {
21266         Roo.log('img onclick');
21267         this.fireEvent('click', this, e);
21268     }
21269    
21270 });
21271
21272  
21273 /*
21274  * - LGPL
21275  *
21276  * numberBox
21277  * 
21278  */
21279 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21280
21281 /**
21282  * @class Roo.bootstrap.dash.NumberBox
21283  * @extends Roo.bootstrap.Component
21284  * Bootstrap NumberBox class
21285  * @cfg {String} headline Box headline
21286  * @cfg {String} content Box content
21287  * @cfg {String} icon Box icon
21288  * @cfg {String} footer Footer text
21289  * @cfg {String} fhref Footer href
21290  * 
21291  * @constructor
21292  * Create a new NumberBox
21293  * @param {Object} config The config object
21294  */
21295
21296
21297 Roo.bootstrap.dash.NumberBox = function(config){
21298     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21299     
21300 };
21301
21302 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21303     
21304     headline : '',
21305     content : '',
21306     icon : '',
21307     footer : '',
21308     fhref : '',
21309     ficon : '',
21310     
21311     getAutoCreate : function(){
21312         
21313         var cfg = {
21314             tag : 'div',
21315             cls : 'small-box ',
21316             cn : [
21317                 {
21318                     tag : 'div',
21319                     cls : 'inner',
21320                     cn :[
21321                         {
21322                             tag : 'h3',
21323                             cls : 'roo-headline',
21324                             html : this.headline
21325                         },
21326                         {
21327                             tag : 'p',
21328                             cls : 'roo-content',
21329                             html : this.content
21330                         }
21331                     ]
21332                 }
21333             ]
21334         }
21335         
21336         if(this.icon){
21337             cfg.cn.push({
21338                 tag : 'div',
21339                 cls : 'icon',
21340                 cn :[
21341                     {
21342                         tag : 'i',
21343                         cls : 'ion ' + this.icon
21344                     }
21345                 ]
21346             });
21347         }
21348         
21349         if(this.footer){
21350             var footer = {
21351                 tag : 'a',
21352                 cls : 'small-box-footer',
21353                 href : this.fhref || '#',
21354                 html : this.footer
21355             };
21356             
21357             cfg.cn.push(footer);
21358             
21359         }
21360         
21361         return  cfg;
21362     },
21363
21364     onRender : function(ct,position){
21365         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21366
21367
21368        
21369                 
21370     },
21371
21372     setHeadline: function (value)
21373     {
21374         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21375     },
21376     
21377     setFooter: function (value, href)
21378     {
21379         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21380         
21381         if(href){
21382             this.el.select('a.small-box-footer',true).first().attr('href', href);
21383         }
21384         
21385     },
21386
21387     setContent: function (value)
21388     {
21389         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21390     },
21391
21392     initEvents: function() 
21393     {   
21394         
21395     }
21396     
21397 });
21398
21399  
21400 /*
21401  * - LGPL
21402  *
21403  * TabBox
21404  * 
21405  */
21406 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21407
21408 /**
21409  * @class Roo.bootstrap.dash.TabBox
21410  * @extends Roo.bootstrap.Component
21411  * Bootstrap TabBox class
21412  * @cfg {String} title Title of the TabBox
21413  * @cfg {String} icon Icon of the TabBox
21414  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21415  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21416  * 
21417  * @constructor
21418  * Create a new TabBox
21419  * @param {Object} config The config object
21420  */
21421
21422
21423 Roo.bootstrap.dash.TabBox = function(config){
21424     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21425     this.addEvents({
21426         // raw events
21427         /**
21428          * @event addpane
21429          * When a pane is added
21430          * @param {Roo.bootstrap.dash.TabPane} pane
21431          */
21432         "addpane" : true,
21433         /**
21434          * @event activatepane
21435          * When a pane is activated
21436          * @param {Roo.bootstrap.dash.TabPane} pane
21437          */
21438         "activatepane" : true
21439         
21440          
21441     });
21442     
21443     this.panes = [];
21444 };
21445
21446 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21447
21448     title : '',
21449     icon : false,
21450     showtabs : true,
21451     tabScrollable : false,
21452     
21453     getChildContainer : function()
21454     {
21455         return this.el.select('.tab-content', true).first();
21456     },
21457     
21458     getAutoCreate : function(){
21459         
21460         var header = {
21461             tag: 'li',
21462             cls: 'pull-left header',
21463             html: this.title,
21464             cn : []
21465         };
21466         
21467         if(this.icon){
21468             header.cn.push({
21469                 tag: 'i',
21470                 cls: 'fa ' + this.icon
21471             });
21472         }
21473         
21474         var h = {
21475             tag: 'ul',
21476             cls: 'nav nav-tabs pull-right',
21477             cn: [
21478                 header
21479             ]
21480         };
21481         
21482         if(this.tabScrollable){
21483             h = {
21484                 tag: 'div',
21485                 cls: 'tab-header',
21486                 cn: [
21487                     {
21488                         tag: 'ul',
21489                         cls: 'nav nav-tabs pull-right',
21490                         cn: [
21491                             header
21492                         ]
21493                     }
21494                 ]
21495             }
21496         }
21497         
21498         var cfg = {
21499             tag: 'div',
21500             cls: 'nav-tabs-custom',
21501             cn: [
21502                 h,
21503                 {
21504                     tag: 'div',
21505                     cls: 'tab-content no-padding',
21506                     cn: []
21507                 }
21508             ]
21509         }
21510
21511         return  cfg;
21512     },
21513     initEvents : function()
21514     {
21515         //Roo.log('add add pane handler');
21516         this.on('addpane', this.onAddPane, this);
21517     },
21518      /**
21519      * Updates the box title
21520      * @param {String} html to set the title to.
21521      */
21522     setTitle : function(value)
21523     {
21524         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21525     },
21526     onAddPane : function(pane)
21527     {
21528         this.panes.push(pane);
21529         //Roo.log('addpane');
21530         //Roo.log(pane);
21531         // tabs are rendere left to right..
21532         if(!this.showtabs){
21533             return;
21534         }
21535         
21536         var ctr = this.el.select('.nav-tabs', true).first();
21537          
21538          
21539         var existing = ctr.select('.nav-tab',true);
21540         var qty = existing.getCount();;
21541         
21542         
21543         var tab = ctr.createChild({
21544             tag : 'li',
21545             cls : 'nav-tab' + (qty ? '' : ' active'),
21546             cn : [
21547                 {
21548                     tag : 'a',
21549                     href:'#',
21550                     html : pane.title
21551                 }
21552             ]
21553         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21554         pane.tab = tab;
21555         
21556         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21557         if (!qty) {
21558             pane.el.addClass('active');
21559         }
21560         
21561                 
21562     },
21563     onTabClick : function(ev,un,ob,pane)
21564     {
21565         //Roo.log('tab - prev default');
21566         ev.preventDefault();
21567         
21568         
21569         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21570         pane.tab.addClass('active');
21571         //Roo.log(pane.title);
21572         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21573         // technically we should have a deactivate event.. but maybe add later.
21574         // and it should not de-activate the selected tab...
21575         this.fireEvent('activatepane', pane);
21576         pane.el.addClass('active');
21577         pane.fireEvent('activate');
21578         
21579         
21580     },
21581     
21582     getActivePane : function()
21583     {
21584         var r = false;
21585         Roo.each(this.panes, function(p) {
21586             if(p.el.hasClass('active')){
21587                 r = p;
21588                 return false;
21589             }
21590             
21591             return;
21592         });
21593         
21594         return r;
21595     }
21596     
21597     
21598 });
21599
21600  
21601 /*
21602  * - LGPL
21603  *
21604  * Tab pane
21605  * 
21606  */
21607 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21608 /**
21609  * @class Roo.bootstrap.TabPane
21610  * @extends Roo.bootstrap.Component
21611  * Bootstrap TabPane class
21612  * @cfg {Boolean} active (false | true) Default false
21613  * @cfg {String} title title of panel
21614
21615  * 
21616  * @constructor
21617  * Create a new TabPane
21618  * @param {Object} config The config object
21619  */
21620
21621 Roo.bootstrap.dash.TabPane = function(config){
21622     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21623     
21624     this.addEvents({
21625         // raw events
21626         /**
21627          * @event activate
21628          * When a pane is activated
21629          * @param {Roo.bootstrap.dash.TabPane} pane
21630          */
21631         "activate" : true
21632          
21633     });
21634 };
21635
21636 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21637     
21638     active : false,
21639     title : '',
21640     
21641     // the tabBox that this is attached to.
21642     tab : false,
21643      
21644     getAutoCreate : function() 
21645     {
21646         var cfg = {
21647             tag: 'div',
21648             cls: 'tab-pane'
21649         }
21650         
21651         if(this.active){
21652             cfg.cls += ' active';
21653         }
21654         
21655         return cfg;
21656     },
21657     initEvents  : function()
21658     {
21659         //Roo.log('trigger add pane handler');
21660         this.parent().fireEvent('addpane', this)
21661     },
21662     
21663      /**
21664      * Updates the tab title 
21665      * @param {String} html to set the title to.
21666      */
21667     setTitle: function(str)
21668     {
21669         if (!this.tab) {
21670             return;
21671         }
21672         this.title = str;
21673         this.tab.select('a', true).first().dom.innerHTML = str;
21674         
21675     }
21676     
21677     
21678     
21679 });
21680
21681  
21682
21683
21684  /*
21685  * - LGPL
21686  *
21687  * menu
21688  * 
21689  */
21690 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21691
21692 /**
21693  * @class Roo.bootstrap.menu.Menu
21694  * @extends Roo.bootstrap.Component
21695  * Bootstrap Menu class - container for Menu
21696  * @cfg {String} html Text of the menu
21697  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21698  * @cfg {String} icon Font awesome icon
21699  * @cfg {String} pos Menu align to (top | bottom) default bottom
21700  * 
21701  * 
21702  * @constructor
21703  * Create a new Menu
21704  * @param {Object} config The config object
21705  */
21706
21707
21708 Roo.bootstrap.menu.Menu = function(config){
21709     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21710     
21711     this.addEvents({
21712         /**
21713          * @event beforeshow
21714          * Fires before this menu is displayed
21715          * @param {Roo.bootstrap.menu.Menu} this
21716          */
21717         beforeshow : true,
21718         /**
21719          * @event beforehide
21720          * Fires before this menu is hidden
21721          * @param {Roo.bootstrap.menu.Menu} this
21722          */
21723         beforehide : true,
21724         /**
21725          * @event show
21726          * Fires after this menu is displayed
21727          * @param {Roo.bootstrap.menu.Menu} this
21728          */
21729         show : true,
21730         /**
21731          * @event hide
21732          * Fires after this menu is hidden
21733          * @param {Roo.bootstrap.menu.Menu} this
21734          */
21735         hide : true,
21736         /**
21737          * @event click
21738          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21739          * @param {Roo.bootstrap.menu.Menu} this
21740          * @param {Roo.EventObject} e
21741          */
21742         click : true
21743     });
21744     
21745 };
21746
21747 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21748     
21749     submenu : false,
21750     html : '',
21751     weight : 'default',
21752     icon : false,
21753     pos : 'bottom',
21754     
21755     
21756     getChildContainer : function() {
21757         if(this.isSubMenu){
21758             return this.el;
21759         }
21760         
21761         return this.el.select('ul.dropdown-menu', true).first();  
21762     },
21763     
21764     getAutoCreate : function()
21765     {
21766         var text = [
21767             {
21768                 tag : 'span',
21769                 cls : 'roo-menu-text',
21770                 html : this.html
21771             }
21772         ];
21773         
21774         if(this.icon){
21775             text.unshift({
21776                 tag : 'i',
21777                 cls : 'fa ' + this.icon
21778             })
21779         }
21780         
21781         
21782         var cfg = {
21783             tag : 'div',
21784             cls : 'btn-group',
21785             cn : [
21786                 {
21787                     tag : 'button',
21788                     cls : 'dropdown-button btn btn-' + this.weight,
21789                     cn : text
21790                 },
21791                 {
21792                     tag : 'button',
21793                     cls : 'dropdown-toggle btn btn-' + this.weight,
21794                     cn : [
21795                         {
21796                             tag : 'span',
21797                             cls : 'caret'
21798                         }
21799                     ]
21800                 },
21801                 {
21802                     tag : 'ul',
21803                     cls : 'dropdown-menu'
21804                 }
21805             ]
21806             
21807         };
21808         
21809         if(this.pos == 'top'){
21810             cfg.cls += ' dropup';
21811         }
21812         
21813         if(this.isSubMenu){
21814             cfg = {
21815                 tag : 'ul',
21816                 cls : 'dropdown-menu'
21817             }
21818         }
21819         
21820         return cfg;
21821     },
21822     
21823     onRender : function(ct, position)
21824     {
21825         this.isSubMenu = ct.hasClass('dropdown-submenu');
21826         
21827         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21828     },
21829     
21830     initEvents : function() 
21831     {
21832         if(this.isSubMenu){
21833             return;
21834         }
21835         
21836         this.hidden = true;
21837         
21838         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21839         this.triggerEl.on('click', this.onTriggerPress, this);
21840         
21841         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21842         this.buttonEl.on('click', this.onClick, this);
21843         
21844     },
21845     
21846     list : function()
21847     {
21848         if(this.isSubMenu){
21849             return this.el;
21850         }
21851         
21852         return this.el.select('ul.dropdown-menu', true).first();
21853     },
21854     
21855     onClick : function(e)
21856     {
21857         this.fireEvent("click", this, e);
21858     },
21859     
21860     onTriggerPress  : function(e)
21861     {   
21862         if (this.isVisible()) {
21863             this.hide();
21864         } else {
21865             this.show();
21866         }
21867     },
21868     
21869     isVisible : function(){
21870         return !this.hidden;
21871     },
21872     
21873     show : function()
21874     {
21875         this.fireEvent("beforeshow", this);
21876         
21877         this.hidden = false;
21878         this.el.addClass('open');
21879         
21880         Roo.get(document).on("mouseup", this.onMouseUp, this);
21881         
21882         this.fireEvent("show", this);
21883         
21884         
21885     },
21886     
21887     hide : function()
21888     {
21889         this.fireEvent("beforehide", this);
21890         
21891         this.hidden = true;
21892         this.el.removeClass('open');
21893         
21894         Roo.get(document).un("mouseup", this.onMouseUp);
21895         
21896         this.fireEvent("hide", this);
21897     },
21898     
21899     onMouseUp : function()
21900     {
21901         this.hide();
21902     }
21903     
21904 });
21905
21906  
21907  /*
21908  * - LGPL
21909  *
21910  * menu item
21911  * 
21912  */
21913 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21914
21915 /**
21916  * @class Roo.bootstrap.menu.Item
21917  * @extends Roo.bootstrap.Component
21918  * Bootstrap MenuItem class
21919  * @cfg {Boolean} submenu (true | false) default false
21920  * @cfg {String} html text of the item
21921  * @cfg {String} href the link
21922  * @cfg {Boolean} disable (true | false) default false
21923  * @cfg {Boolean} preventDefault (true | false) default true
21924  * @cfg {String} icon Font awesome icon
21925  * @cfg {String} pos Submenu align to (left | right) default right 
21926  * 
21927  * 
21928  * @constructor
21929  * Create a new Item
21930  * @param {Object} config The config object
21931  */
21932
21933
21934 Roo.bootstrap.menu.Item = function(config){
21935     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21936     this.addEvents({
21937         /**
21938          * @event mouseover
21939          * Fires when the mouse is hovering over this menu
21940          * @param {Roo.bootstrap.menu.Item} this
21941          * @param {Roo.EventObject} e
21942          */
21943         mouseover : true,
21944         /**
21945          * @event mouseout
21946          * Fires when the mouse exits this menu
21947          * @param {Roo.bootstrap.menu.Item} this
21948          * @param {Roo.EventObject} e
21949          */
21950         mouseout : true,
21951         // raw events
21952         /**
21953          * @event click
21954          * The raw click event for the entire grid.
21955          * @param {Roo.EventObject} e
21956          */
21957         click : true
21958     });
21959 };
21960
21961 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21962     
21963     submenu : false,
21964     href : '',
21965     html : '',
21966     preventDefault: true,
21967     disable : false,
21968     icon : false,
21969     pos : 'right',
21970     
21971     getAutoCreate : function()
21972     {
21973         var text = [
21974             {
21975                 tag : 'span',
21976                 cls : 'roo-menu-item-text',
21977                 html : this.html
21978             }
21979         ];
21980         
21981         if(this.icon){
21982             text.unshift({
21983                 tag : 'i',
21984                 cls : 'fa ' + this.icon
21985             })
21986         }
21987         
21988         var cfg = {
21989             tag : 'li',
21990             cn : [
21991                 {
21992                     tag : 'a',
21993                     href : this.href || '#',
21994                     cn : text
21995                 }
21996             ]
21997         };
21998         
21999         if(this.disable){
22000             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22001         }
22002         
22003         if(this.submenu){
22004             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22005             
22006             if(this.pos == 'left'){
22007                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22008             }
22009         }
22010         
22011         return cfg;
22012     },
22013     
22014     initEvents : function() 
22015     {
22016         this.el.on('mouseover', this.onMouseOver, this);
22017         this.el.on('mouseout', this.onMouseOut, this);
22018         
22019         this.el.select('a', true).first().on('click', this.onClick, this);
22020         
22021     },
22022     
22023     onClick : function(e)
22024     {
22025         if(this.preventDefault){
22026             e.preventDefault();
22027         }
22028         
22029         this.fireEvent("click", this, e);
22030     },
22031     
22032     onMouseOver : function(e)
22033     {
22034         if(this.submenu && this.pos == 'left'){
22035             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22036         }
22037         
22038         this.fireEvent("mouseover", this, e);
22039     },
22040     
22041     onMouseOut : function(e)
22042     {
22043         this.fireEvent("mouseout", this, e);
22044     }
22045 });
22046
22047  
22048
22049  /*
22050  * - LGPL
22051  *
22052  * menu separator
22053  * 
22054  */
22055 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22056
22057 /**
22058  * @class Roo.bootstrap.menu.Separator
22059  * @extends Roo.bootstrap.Component
22060  * Bootstrap Separator class
22061  * 
22062  * @constructor
22063  * Create a new Separator
22064  * @param {Object} config The config object
22065  */
22066
22067
22068 Roo.bootstrap.menu.Separator = function(config){
22069     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22070 };
22071
22072 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22073     
22074     getAutoCreate : function(){
22075         var cfg = {
22076             tag : 'li',
22077             cls: 'divider'
22078         };
22079         
22080         return cfg;
22081     }
22082    
22083 });
22084
22085  
22086
22087  /*
22088  * - LGPL
22089  *
22090  * Tooltip
22091  * 
22092  */
22093
22094 /**
22095  * @class Roo.bootstrap.Tooltip
22096  * Bootstrap Tooltip class
22097  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22098  * to determine which dom element triggers the tooltip.
22099  * 
22100  * It needs to add support for additional attributes like tooltip-position
22101  * 
22102  * @constructor
22103  * Create a new Toolti
22104  * @param {Object} config The config object
22105  */
22106
22107 Roo.bootstrap.Tooltip = function(config){
22108     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22109 };
22110
22111 Roo.apply(Roo.bootstrap.Tooltip, {
22112     /**
22113      * @function init initialize tooltip monitoring.
22114      * @static
22115      */
22116     currentEl : false,
22117     currentTip : false,
22118     currentRegion : false,
22119     
22120     //  init : delay?
22121     
22122     init : function()
22123     {
22124         Roo.get(document).on('mouseover', this.enter ,this);
22125         Roo.get(document).on('mouseout', this.leave, this);
22126          
22127         
22128         this.currentTip = new Roo.bootstrap.Tooltip();
22129     },
22130     
22131     enter : function(ev)
22132     {
22133         var dom = ev.getTarget();
22134         
22135         //Roo.log(['enter',dom]);
22136         var el = Roo.fly(dom);
22137         if (this.currentEl) {
22138             //Roo.log(dom);
22139             //Roo.log(this.currentEl);
22140             //Roo.log(this.currentEl.contains(dom));
22141             if (this.currentEl == el) {
22142                 return;
22143             }
22144             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22145                 return;
22146             }
22147
22148         }
22149         
22150         
22151         
22152         if (this.currentTip.el) {
22153             this.currentTip.el.hide(); // force hiding...
22154         }    
22155         //Roo.log(ev);
22156         var bindEl = el;
22157         
22158         // you can not look for children, as if el is the body.. then everythign is the child..
22159         if (!el.attr('tooltip')) { //
22160             if (!el.select("[tooltip]").elements.length) {
22161                 return;
22162             }
22163             // is the mouse over this child...?
22164             bindEl = el.select("[tooltip]").first();
22165             var xy = ev.getXY();
22166             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22167                 //Roo.log("not in region.");
22168                 return;
22169             }
22170             //Roo.log("child element over..");
22171             
22172         }
22173         this.currentEl = bindEl;
22174         this.currentTip.bind(bindEl);
22175         this.currentRegion = Roo.lib.Region.getRegion(dom);
22176         this.currentTip.enter();
22177         
22178     },
22179     leave : function(ev)
22180     {
22181         var dom = ev.getTarget();
22182         //Roo.log(['leave',dom]);
22183         if (!this.currentEl) {
22184             return;
22185         }
22186         
22187         
22188         if (dom != this.currentEl.dom) {
22189             return;
22190         }
22191         var xy = ev.getXY();
22192         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22193             return;
22194         }
22195         // only activate leave if mouse cursor is outside... bounding box..
22196         
22197         
22198         
22199         
22200         if (this.currentTip) {
22201             this.currentTip.leave();
22202         }
22203         //Roo.log('clear currentEl');
22204         this.currentEl = false;
22205         
22206         
22207     },
22208     alignment : {
22209         'left' : ['r-l', [-2,0], 'right'],
22210         'right' : ['l-r', [2,0], 'left'],
22211         'bottom' : ['t-b', [0,2], 'top'],
22212         'top' : [ 'b-t', [0,-2], 'bottom']
22213     }
22214     
22215 });
22216
22217
22218 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22219     
22220     
22221     bindEl : false,
22222     
22223     delay : null, // can be { show : 300 , hide: 500}
22224     
22225     timeout : null,
22226     
22227     hoverState : null, //???
22228     
22229     placement : 'bottom', 
22230     
22231     getAutoCreate : function(){
22232     
22233         var cfg = {
22234            cls : 'tooltip',
22235            role : 'tooltip',
22236            cn : [
22237                 {
22238                     cls : 'tooltip-arrow'
22239                 },
22240                 {
22241                     cls : 'tooltip-inner'
22242                 }
22243            ]
22244         };
22245         
22246         return cfg;
22247     },
22248     bind : function(el)
22249     {
22250         this.bindEl = el;
22251     },
22252       
22253     
22254     enter : function () {
22255        
22256         if (this.timeout != null) {
22257             clearTimeout(this.timeout);
22258         }
22259         
22260         this.hoverState = 'in';
22261          //Roo.log("enter - show");
22262         if (!this.delay || !this.delay.show) {
22263             this.show();
22264             return;
22265         }
22266         var _t = this;
22267         this.timeout = setTimeout(function () {
22268             if (_t.hoverState == 'in') {
22269                 _t.show();
22270             }
22271         }, this.delay.show);
22272     },
22273     leave : function()
22274     {
22275         clearTimeout(this.timeout);
22276     
22277         this.hoverState = 'out';
22278          if (!this.delay || !this.delay.hide) {
22279             this.hide();
22280             return;
22281         }
22282        
22283         var _t = this;
22284         this.timeout = setTimeout(function () {
22285             //Roo.log("leave - timeout");
22286             
22287             if (_t.hoverState == 'out') {
22288                 _t.hide();
22289                 Roo.bootstrap.Tooltip.currentEl = false;
22290             }
22291         }, delay);
22292     },
22293     
22294     show : function ()
22295     {
22296         if (!this.el) {
22297             this.render(document.body);
22298         }
22299         // set content.
22300         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22301         
22302         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22303         
22304         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22305         
22306         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22307         
22308         var placement = typeof this.placement == 'function' ?
22309             this.placement.call(this, this.el, on_el) :
22310             this.placement;
22311             
22312         var autoToken = /\s?auto?\s?/i;
22313         var autoPlace = autoToken.test(placement);
22314         if (autoPlace) {
22315             placement = placement.replace(autoToken, '') || 'top';
22316         }
22317         
22318         //this.el.detach()
22319         //this.el.setXY([0,0]);
22320         this.el.show();
22321         //this.el.dom.style.display='block';
22322         this.el.addClass(placement);
22323         
22324         //this.el.appendTo(on_el);
22325         
22326         var p = this.getPosition();
22327         var box = this.el.getBox();
22328         
22329         if (autoPlace) {
22330             // fixme..
22331         }
22332         var align = Roo.bootstrap.Tooltip.alignment[placement];
22333         this.el.alignTo(this.bindEl, align[0],align[1]);
22334         //var arrow = this.el.select('.arrow',true).first();
22335         //arrow.set(align[2], 
22336         
22337         this.el.addClass('in fade');
22338         this.hoverState = null;
22339         
22340         if (this.el.hasClass('fade')) {
22341             // fade it?
22342         }
22343         
22344     },
22345     hide : function()
22346     {
22347          
22348         if (!this.el) {
22349             return;
22350         }
22351         //this.el.setXY([0,0]);
22352         this.el.removeClass('in');
22353         //this.el.hide();
22354         
22355     }
22356     
22357 });
22358  
22359
22360  /*
22361  * - LGPL
22362  *
22363  * Location Picker
22364  * 
22365  */
22366
22367 /**
22368  * @class Roo.bootstrap.LocationPicker
22369  * @extends Roo.bootstrap.Component
22370  * Bootstrap LocationPicker class
22371  * @cfg {Number} latitude Position when init default 0
22372  * @cfg {Number} longitude Position when init default 0
22373  * @cfg {Number} zoom default 15
22374  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22375  * @cfg {Boolean} mapTypeControl default false
22376  * @cfg {Boolean} disableDoubleClickZoom default false
22377  * @cfg {Boolean} scrollwheel default true
22378  * @cfg {Boolean} streetViewControl default false
22379  * @cfg {Number} radius default 0
22380  * @cfg {String} locationName
22381  * @cfg {Boolean} draggable default true
22382  * @cfg {Boolean} enableAutocomplete default false
22383  * @cfg {Boolean} enableReverseGeocode default true
22384  * @cfg {String} markerTitle
22385  * 
22386  * @constructor
22387  * Create a new LocationPicker
22388  * @param {Object} config The config object
22389  */
22390
22391
22392 Roo.bootstrap.LocationPicker = function(config){
22393     
22394     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22395     
22396     this.addEvents({
22397         /**
22398          * @event initial
22399          * Fires when the picker initialized.
22400          * @param {Roo.bootstrap.LocationPicker} this
22401          * @param {Google Location} location
22402          */
22403         initial : true,
22404         /**
22405          * @event positionchanged
22406          * Fires when the picker position changed.
22407          * @param {Roo.bootstrap.LocationPicker} this
22408          * @param {Google Location} location
22409          */
22410         positionchanged : true,
22411         /**
22412          * @event resize
22413          * Fires when the map resize.
22414          * @param {Roo.bootstrap.LocationPicker} this
22415          */
22416         resize : true,
22417         /**
22418          * @event show
22419          * Fires when the map show.
22420          * @param {Roo.bootstrap.LocationPicker} this
22421          */
22422         show : true,
22423         /**
22424          * @event hide
22425          * Fires when the map hide.
22426          * @param {Roo.bootstrap.LocationPicker} this
22427          */
22428         hide : true,
22429         /**
22430          * @event mapClick
22431          * Fires when click the map.
22432          * @param {Roo.bootstrap.LocationPicker} this
22433          * @param {Map event} e
22434          */
22435         mapClick : true,
22436         /**
22437          * @event mapRightClick
22438          * Fires when right click the map.
22439          * @param {Roo.bootstrap.LocationPicker} this
22440          * @param {Map event} e
22441          */
22442         mapRightClick : true,
22443         /**
22444          * @event markerClick
22445          * Fires when click the marker.
22446          * @param {Roo.bootstrap.LocationPicker} this
22447          * @param {Map event} e
22448          */
22449         markerClick : true,
22450         /**
22451          * @event markerRightClick
22452          * Fires when right click the marker.
22453          * @param {Roo.bootstrap.LocationPicker} this
22454          * @param {Map event} e
22455          */
22456         markerRightClick : true,
22457         /**
22458          * @event OverlayViewDraw
22459          * Fires when OverlayView Draw
22460          * @param {Roo.bootstrap.LocationPicker} this
22461          */
22462         OverlayViewDraw : true,
22463         /**
22464          * @event OverlayViewOnAdd
22465          * Fires when OverlayView Draw
22466          * @param {Roo.bootstrap.LocationPicker} this
22467          */
22468         OverlayViewOnAdd : true,
22469         /**
22470          * @event OverlayViewOnRemove
22471          * Fires when OverlayView Draw
22472          * @param {Roo.bootstrap.LocationPicker} this
22473          */
22474         OverlayViewOnRemove : true,
22475         /**
22476          * @event OverlayViewShow
22477          * Fires when OverlayView Draw
22478          * @param {Roo.bootstrap.LocationPicker} this
22479          * @param {Pixel} cpx
22480          */
22481         OverlayViewShow : true,
22482         /**
22483          * @event OverlayViewHide
22484          * Fires when OverlayView Draw
22485          * @param {Roo.bootstrap.LocationPicker} this
22486          */
22487         OverlayViewHide : true
22488     });
22489         
22490 };
22491
22492 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22493     
22494     gMapContext: false,
22495     
22496     latitude: 0,
22497     longitude: 0,
22498     zoom: 15,
22499     mapTypeId: false,
22500     mapTypeControl: false,
22501     disableDoubleClickZoom: false,
22502     scrollwheel: true,
22503     streetViewControl: false,
22504     radius: 0,
22505     locationName: '',
22506     draggable: true,
22507     enableAutocomplete: false,
22508     enableReverseGeocode: true,
22509     markerTitle: '',
22510     
22511     getAutoCreate: function()
22512     {
22513
22514         var cfg = {
22515             tag: 'div',
22516             cls: 'roo-location-picker'
22517         };
22518         
22519         return cfg
22520     },
22521     
22522     initEvents: function(ct, position)
22523     {       
22524         if(!this.el.getWidth() || this.isApplied()){
22525             return;
22526         }
22527         
22528         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22529         
22530         this.initial();
22531     },
22532     
22533     initial: function()
22534     {
22535         if(!this.mapTypeId){
22536             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22537         }
22538         
22539         this.gMapContext = this.GMapContext();
22540         
22541         this.initOverlayView();
22542         
22543         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22544         
22545         var _this = this;
22546                 
22547         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22548             _this.setPosition(_this.gMapContext.marker.position);
22549         });
22550         
22551         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22552             _this.fireEvent('mapClick', this, event);
22553             
22554         });
22555
22556         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22557             _this.fireEvent('mapRightClick', this, event);
22558             
22559         });
22560         
22561         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22562             _this.fireEvent('markerClick', this, event);
22563             
22564         });
22565
22566         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22567             _this.fireEvent('markerRightClick', this, event);
22568             
22569         });
22570         
22571         this.setPosition(this.gMapContext.location);
22572         
22573         this.fireEvent('initial', this, this.gMapContext.location);
22574     },
22575     
22576     initOverlayView: function()
22577     {
22578         var _this = this;
22579         
22580         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22581             
22582             draw: function()
22583             {
22584                 _this.fireEvent('OverlayViewDraw', _this);
22585             },
22586             
22587             onAdd: function()
22588             {
22589                 _this.fireEvent('OverlayViewOnAdd', _this);
22590             },
22591             
22592             onRemove: function()
22593             {
22594                 _this.fireEvent('OverlayViewOnRemove', _this);
22595             },
22596             
22597             show: function(cpx)
22598             {
22599                 _this.fireEvent('OverlayViewShow', _this, cpx);
22600             },
22601             
22602             hide: function()
22603             {
22604                 _this.fireEvent('OverlayViewHide', _this);
22605             }
22606             
22607         });
22608     },
22609     
22610     fromLatLngToContainerPixel: function(event)
22611     {
22612         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22613     },
22614     
22615     isApplied: function() 
22616     {
22617         return this.getGmapContext() == false ? false : true;
22618     },
22619     
22620     getGmapContext: function() 
22621     {
22622         return this.gMapContext
22623     },
22624     
22625     GMapContext: function() 
22626     {
22627         var position = new google.maps.LatLng(this.latitude, this.longitude);
22628         
22629         var _map = new google.maps.Map(this.el.dom, {
22630             center: position,
22631             zoom: this.zoom,
22632             mapTypeId: this.mapTypeId,
22633             mapTypeControl: this.mapTypeControl,
22634             disableDoubleClickZoom: this.disableDoubleClickZoom,
22635             scrollwheel: this.scrollwheel,
22636             streetViewControl: this.streetViewControl,
22637             locationName: this.locationName,
22638             draggable: this.draggable,
22639             enableAutocomplete: this.enableAutocomplete,
22640             enableReverseGeocode: this.enableReverseGeocode
22641         });
22642         
22643         var _marker = new google.maps.Marker({
22644             position: position,
22645             map: _map,
22646             title: this.markerTitle,
22647             draggable: this.draggable
22648         });
22649         
22650         return {
22651             map: _map,
22652             marker: _marker,
22653             circle: null,
22654             location: position,
22655             radius: this.radius,
22656             locationName: this.locationName,
22657             addressComponents: {
22658                 formatted_address: null,
22659                 addressLine1: null,
22660                 addressLine2: null,
22661                 streetName: null,
22662                 streetNumber: null,
22663                 city: null,
22664                 district: null,
22665                 state: null,
22666                 stateOrProvince: null
22667             },
22668             settings: this,
22669             domContainer: this.el.dom,
22670             geodecoder: new google.maps.Geocoder()
22671         };
22672     },
22673     
22674     drawCircle: function(center, radius, options) 
22675     {
22676         if (this.gMapContext.circle != null) {
22677             this.gMapContext.circle.setMap(null);
22678         }
22679         if (radius > 0) {
22680             radius *= 1;
22681             options = Roo.apply({}, options, {
22682                 strokeColor: "#0000FF",
22683                 strokeOpacity: .35,
22684                 strokeWeight: 2,
22685                 fillColor: "#0000FF",
22686                 fillOpacity: .2
22687             });
22688             
22689             options.map = this.gMapContext.map;
22690             options.radius = radius;
22691             options.center = center;
22692             this.gMapContext.circle = new google.maps.Circle(options);
22693             return this.gMapContext.circle;
22694         }
22695         
22696         return null;
22697     },
22698     
22699     setPosition: function(location) 
22700     {
22701         this.gMapContext.location = location;
22702         this.gMapContext.marker.setPosition(location);
22703         this.gMapContext.map.panTo(location);
22704         this.drawCircle(location, this.gMapContext.radius, {});
22705         
22706         var _this = this;
22707         
22708         if (this.gMapContext.settings.enableReverseGeocode) {
22709             this.gMapContext.geodecoder.geocode({
22710                 latLng: this.gMapContext.location
22711             }, function(results, status) {
22712                 
22713                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22714                     _this.gMapContext.locationName = results[0].formatted_address;
22715                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22716                     
22717                     _this.fireEvent('positionchanged', this, location);
22718                 }
22719             });
22720             
22721             return;
22722         }
22723         
22724         this.fireEvent('positionchanged', this, location);
22725     },
22726     
22727     resize: function()
22728     {
22729         google.maps.event.trigger(this.gMapContext.map, "resize");
22730         
22731         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22732         
22733         this.fireEvent('resize', this);
22734     },
22735     
22736     setPositionByLatLng: function(latitude, longitude)
22737     {
22738         this.setPosition(new google.maps.LatLng(latitude, longitude));
22739     },
22740     
22741     getCurrentPosition: function() 
22742     {
22743         return {
22744             latitude: this.gMapContext.location.lat(),
22745             longitude: this.gMapContext.location.lng()
22746         };
22747     },
22748     
22749     getAddressName: function() 
22750     {
22751         return this.gMapContext.locationName;
22752     },
22753     
22754     getAddressComponents: function() 
22755     {
22756         return this.gMapContext.addressComponents;
22757     },
22758     
22759     address_component_from_google_geocode: function(address_components) 
22760     {
22761         var result = {};
22762         
22763         for (var i = 0; i < address_components.length; i++) {
22764             var component = address_components[i];
22765             if (component.types.indexOf("postal_code") >= 0) {
22766                 result.postalCode = component.short_name;
22767             } else if (component.types.indexOf("street_number") >= 0) {
22768                 result.streetNumber = component.short_name;
22769             } else if (component.types.indexOf("route") >= 0) {
22770                 result.streetName = component.short_name;
22771             } else if (component.types.indexOf("neighborhood") >= 0) {
22772                 result.city = component.short_name;
22773             } else if (component.types.indexOf("locality") >= 0) {
22774                 result.city = component.short_name;
22775             } else if (component.types.indexOf("sublocality") >= 0) {
22776                 result.district = component.short_name;
22777             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22778                 result.stateOrProvince = component.short_name;
22779             } else if (component.types.indexOf("country") >= 0) {
22780                 result.country = component.short_name;
22781             }
22782         }
22783         
22784         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22785         result.addressLine2 = "";
22786         return result;
22787     },
22788     
22789     setZoomLevel: function(zoom)
22790     {
22791         this.gMapContext.map.setZoom(zoom);
22792     },
22793     
22794     show: function()
22795     {
22796         if(!this.el){
22797             return;
22798         }
22799         
22800         this.el.show();
22801         
22802         this.resize();
22803         
22804         this.fireEvent('show', this);
22805     },
22806     
22807     hide: function()
22808     {
22809         if(!this.el){
22810             return;
22811         }
22812         
22813         this.el.hide();
22814         
22815         this.fireEvent('hide', this);
22816     }
22817     
22818 });
22819
22820 Roo.apply(Roo.bootstrap.LocationPicker, {
22821     
22822     OverlayView : function(map, options)
22823     {
22824         options = options || {};
22825         
22826         this.setMap(map);
22827     }
22828     
22829     
22830 });/*
22831  * - LGPL
22832  *
22833  * Alert
22834  * 
22835  */
22836
22837 /**
22838  * @class Roo.bootstrap.Alert
22839  * @extends Roo.bootstrap.Component
22840  * Bootstrap Alert class
22841  * @cfg {String} title The title of alert
22842  * @cfg {String} html The content of alert
22843  * @cfg {String} weight (  success | info | warning | danger )
22844  * @cfg {String} faicon font-awesomeicon
22845  * 
22846  * @constructor
22847  * Create a new alert
22848  * @param {Object} config The config object
22849  */
22850
22851
22852 Roo.bootstrap.Alert = function(config){
22853     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22854     
22855 };
22856
22857 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22858     
22859     title: '',
22860     html: '',
22861     weight: false,
22862     faicon: false,
22863     
22864     getAutoCreate : function()
22865     {
22866         
22867         var cfg = {
22868             tag : 'div',
22869             cls : 'alert',
22870             cn : [
22871                 {
22872                     tag : 'i',
22873                     cls : 'roo-alert-icon'
22874                     
22875                 },
22876                 {
22877                     tag : 'b',
22878                     cls : 'roo-alert-title',
22879                     html : this.title
22880                 },
22881                 {
22882                     tag : 'span',
22883                     cls : 'roo-alert-text',
22884                     html : this.html
22885                 }
22886             ]
22887         };
22888         
22889         if(this.faicon){
22890             cfg.cn[0].cls += ' fa ' + this.faicon;
22891         }
22892         
22893         if(this.weight){
22894             cfg.cls += ' alert-' + this.weight;
22895         }
22896         
22897         return cfg;
22898     },
22899     
22900     initEvents: function() 
22901     {
22902         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22903     },
22904     
22905     setTitle : function(str)
22906     {
22907         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22908     },
22909     
22910     setText : function(str)
22911     {
22912         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22913     },
22914     
22915     setWeight : function(weight)
22916     {
22917         if(this.weight){
22918             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22919         }
22920         
22921         this.weight = weight;
22922         
22923         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22924     },
22925     
22926     setIcon : function(icon)
22927     {
22928         if(this.faicon){
22929             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22930         }
22931         
22932         this.faicon = icon
22933         
22934         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22935     },
22936     
22937     hide: function() 
22938     {
22939         this.el.hide();   
22940     },
22941     
22942     show: function() 
22943     {  
22944         this.el.show();   
22945     }
22946     
22947 });
22948
22949