Merge branch 'master' of http://git.roojs.com/roojs1
[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     
346 });
347
348  /*
349  * - LGPL
350  *
351  * Body
352  * 
353  */
354
355 /**
356  * @class Roo.bootstrap.Body
357  * @extends Roo.bootstrap.Component
358  * Bootstrap Body class
359  * 
360  * @constructor
361  * Create a new body
362  * @param {Object} config The config object
363  */
364
365 Roo.bootstrap.Body = function(config){
366     Roo.bootstrap.Body.superclass.constructor.call(this, config);
367     this.el = Roo.get(document.body);
368     if (this.cls && this.cls.length) {
369         Roo.get(document.body).addClass(this.cls);
370     }
371 };
372
373 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
374       
375         autoCreate : {
376         cls: 'container'
377     },
378     onRender : function(ct, position)
379     {
380        /* Roo.log("Roo.bootstrap.Body - onRender");
381         if (this.cls && this.cls.length) {
382             Roo.get(document.body).addClass(this.cls);
383         }
384         // style??? xttr???
385         */
386     }
387     
388     
389  
390    
391 });
392
393  /*
394  * - LGPL
395  *
396  * button group
397  * 
398  */
399
400
401 /**
402  * @class Roo.bootstrap.ButtonGroup
403  * @extends Roo.bootstrap.Component
404  * Bootstrap ButtonGroup class
405  * @cfg {String} size lg | sm | xs (default empty normal)
406  * @cfg {String} align vertical | justified  (default none)
407  * @cfg {String} direction up | down (default down)
408  * @cfg {Boolean} toolbar false | true
409  * @cfg {Boolean} btn true | false
410  * 
411  * 
412  * @constructor
413  * Create a new Input
414  * @param {Object} config The config object
415  */
416
417 Roo.bootstrap.ButtonGroup = function(config){
418     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
419 };
420
421 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
422     
423     size: '',
424     align: '',
425     direction: '',
426     toolbar: false,
427     btn: true,
428
429     getAutoCreate : function(){
430         var cfg = {
431             cls: 'btn-group',
432             html : null
433         }
434         
435         cfg.html = this.html || cfg.html;
436         
437         if (this.toolbar) {
438             cfg = {
439                 cls: 'btn-toolbar',
440                 html: null
441             }
442             
443             return cfg;
444         }
445         
446         if (['vertical','justified'].indexOf(this.align)!==-1) {
447             cfg.cls = 'btn-group-' + this.align;
448             
449             if (this.align == 'justified') {
450                 console.log(this.items);
451             }
452         }
453         
454         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
455             cfg.cls += ' btn-group-' + this.size;
456         }
457         
458         if (this.direction == 'up') {
459             cfg.cls += ' dropup' ;
460         }
461         
462         return cfg;
463     }
464    
465 });
466
467  /*
468  * - LGPL
469  *
470  * button
471  * 
472  */
473
474 /**
475  * @class Roo.bootstrap.Button
476  * @extends Roo.bootstrap.Component
477  * Bootstrap Button class
478  * @cfg {String} html The button content
479  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
480  * @cfg {String} size ( lg | sm | xs)
481  * @cfg {String} tag ( a | input | submit)
482  * @cfg {String} href empty or href
483  * @cfg {Boolean} disabled default false;
484  * @cfg {Boolean} isClose default false;
485  * @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)
486  * @cfg {String} badge text for badge
487  * @cfg {String} theme default 
488  * @cfg {Boolean} inverse 
489  * @cfg {Boolean} toggle 
490  * @cfg {String} ontext text for on toggle state
491  * @cfg {String} offtext text for off toggle state
492  * @cfg {Boolean} defaulton 
493  * @cfg {Boolean} preventDefault  default true
494  * @cfg {Boolean} removeClass remove the standard class..
495  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
496  * 
497  * @constructor
498  * Create a new button
499  * @param {Object} config The config object
500  */
501
502
503 Roo.bootstrap.Button = function(config){
504     Roo.bootstrap.Button.superclass.constructor.call(this, config);
505     this.addEvents({
506         // raw events
507         /**
508          * @event click
509          * When a butotn is pressed
510          * @param {Roo.bootstrap.Button} this
511          * @param {Roo.EventObject} e
512          */
513         "click" : true,
514          /**
515          * @event toggle
516          * After the button has been toggles
517          * @param {Roo.EventObject} e
518          * @param {boolean} pressed (also available as button.pressed)
519          */
520         "toggle" : true
521     });
522 };
523
524 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
525     html: false,
526     active: false,
527     weight: '',
528     size: '',
529     tag: 'button',
530     href: '',
531     disabled: false,
532     isClose: false,
533     glyphicon: '',
534     badge: '',
535     theme: 'default',
536     inverse: false,
537     
538     toggle: false,
539     ontext: 'ON',
540     offtext: 'OFF',
541     defaulton: true,
542     preventDefault: true,
543     removeClass: false,
544     name: false,
545     target: false,
546     
547     
548     pressed : null,
549      
550     
551     getAutoCreate : function(){
552         
553         var cfg = {
554             tag : 'button',
555             cls : 'roo-button',
556             html: ''
557         };
558         
559         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
560             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
561             this.tag = 'button';
562         } else {
563             cfg.tag = this.tag;
564         }
565         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
566         
567         if (this.toggle == true) {
568             cfg={
569                 tag: 'div',
570                 cls: 'slider-frame roo-button',
571                 cn: [
572                     {
573                         tag: 'span',
574                         'data-on-text':'ON',
575                         'data-off-text':'OFF',
576                         cls: 'slider-button',
577                         html: this.offtext
578                     }
579                 ]
580             };
581             
582             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
583                 cfg.cls += ' '+this.weight;
584             }
585             
586             return cfg;
587         }
588         
589         if (this.isClose) {
590             cfg.cls += ' close';
591             
592             cfg["aria-hidden"] = true;
593             
594             cfg.html = "&times;";
595             
596             return cfg;
597         }
598         
599          
600         if (this.theme==='default') {
601             cfg.cls = 'btn roo-button';
602             
603             //if (this.parentType != 'Navbar') {
604             this.weight = this.weight.length ?  this.weight : 'default';
605             //}
606             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
607                 
608                 cfg.cls += ' btn-' + this.weight;
609             }
610         } else if (this.theme==='glow') {
611             
612             cfg.tag = 'a';
613             cfg.cls = 'btn-glow roo-button';
614             
615             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
616                 
617                 cfg.cls += ' ' + this.weight;
618             }
619         }
620    
621         
622         if (this.inverse) {
623             this.cls += ' inverse';
624         }
625         
626         
627         if (this.active) {
628             cfg.cls += ' active';
629         }
630         
631         if (this.disabled) {
632             cfg.disabled = 'disabled';
633         }
634         
635         if (this.items) {
636             Roo.log('changing to ul' );
637             cfg.tag = 'ul';
638             this.glyphicon = 'caret';
639         }
640         
641         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
642          
643         //gsRoo.log(this.parentType);
644         if (this.parentType === 'Navbar' && !this.parent().bar) {
645             Roo.log('changing to li?');
646             
647             cfg.tag = 'li';
648             
649             cfg.cls = '';
650             cfg.cn =  [{
651                 tag : 'a',
652                 cls : 'roo-button',
653                 html : this.html,
654                 href : this.href || '#'
655             }];
656             if (this.menu) {
657                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
658                 cfg.cls += ' dropdown';
659             }   
660             
661             delete cfg.html;
662             
663         }
664         
665        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
666         
667         if (this.glyphicon) {
668             cfg.html = ' ' + cfg.html;
669             
670             cfg.cn = [
671                 {
672                     tag: 'span',
673                     cls: 'glyphicon glyphicon-' + this.glyphicon
674                 }
675             ];
676         }
677         
678         if (this.badge) {
679             cfg.html += ' ';
680             
681             cfg.tag = 'a';
682             
683 //            cfg.cls='btn roo-button';
684             
685             cfg.href=this.href;
686             
687             var value = cfg.html;
688             
689             if(this.glyphicon){
690                 value = {
691                             tag: 'span',
692                             cls: 'glyphicon glyphicon-' + this.glyphicon,
693                             html: this.html
694                         };
695                 
696             }
697             
698             cfg.cn = [
699                 value,
700                 {
701                     tag: 'span',
702                     cls: 'badge',
703                     html: this.badge
704                 }
705             ];
706             
707             cfg.html='';
708         }
709         
710         if (this.menu) {
711             cfg.cls += ' dropdown';
712             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
713         }
714         
715         if (cfg.tag !== 'a' && this.href !== '') {
716             throw "Tag must be a to set href.";
717         } else if (this.href.length > 0) {
718             cfg.href = this.href;
719         }
720         
721         if(this.removeClass){
722             cfg.cls = '';
723         }
724         
725         if(this.target){
726             cfg.target = this.target;
727         }
728         
729         return cfg;
730     },
731     initEvents: function() {
732        // Roo.log('init events?');
733 //        Roo.log(this.el.dom);
734         // add the menu...
735         
736         if (typeof (this.menu) != 'undefined') {
737             this.menu.parentType = this.xtype;
738             this.menu.triggerEl = this.el;
739             this.addxtype(Roo.apply({}, this.menu));
740         }
741
742
743        if (this.el.hasClass('roo-button')) {
744             this.el.on('click', this.onClick, this);
745        } else {
746             this.el.select('.roo-button').on('click', this.onClick, this);
747        }
748        
749        if(this.removeClass){
750            this.el.on('click', this.onClick, this);
751        }
752        
753        this.el.enableDisplayMode();
754         
755     },
756     onClick : function(e)
757     {
758         if (this.disabled) {
759             return;
760         }
761         
762         
763         Roo.log('button on click ');
764         if(this.preventDefault){
765             e.preventDefault();
766         }
767         if (this.pressed === true || this.pressed === false) {
768             this.pressed = !this.pressed;
769             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
770             this.fireEvent('toggle', this, e, this.pressed);
771         }
772         
773         
774         this.fireEvent('click', this, e);
775     },
776     
777     /**
778      * Enables this button
779      */
780     enable : function()
781     {
782         this.disabled = false;
783         this.el.removeClass('disabled');
784     },
785     
786     /**
787      * Disable this button
788      */
789     disable : function()
790     {
791         this.disabled = true;
792         this.el.addClass('disabled');
793     },
794      /**
795      * sets the active state on/off, 
796      * @param {Boolean} state (optional) Force a particular state
797      */
798     setActive : function(v) {
799         
800         this.el[v ? 'addClass' : 'removeClass']('active');
801     },
802      /**
803      * toggles the current active state 
804      */
805     toggleActive : function()
806     {
807        var active = this.el.hasClass('active');
808        this.setActive(!active);
809        
810         
811     },
812     setText : function(str)
813     {
814         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
815     },
816     getText : function()
817     {
818         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
819     },
820     hide: function() {
821        
822      
823         this.el.hide();   
824     },
825     show: function() {
826        
827         this.el.show();   
828     }
829     
830     
831 });
832
833  /*
834  * - LGPL
835  *
836  * column
837  * 
838  */
839
840 /**
841  * @class Roo.bootstrap.Column
842  * @extends Roo.bootstrap.Component
843  * Bootstrap Column class
844  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
845  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
846  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
847  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
848  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
849  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
850  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
851  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
852  *
853  * 
854  * @cfg {Boolean} hidden (true|false) hide the element
855  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
856  * @cfg {String} fa (ban|check|...) font awesome icon
857  * @cfg {Number} fasize (1|2|....) font awsome size
858
859  * @cfg {String} icon (info-sign|check|...) glyphicon name
860
861  * @cfg {String} html content of column.
862  * 
863  * @constructor
864  * Create a new Column
865  * @param {Object} config The config object
866  */
867
868 Roo.bootstrap.Column = function(config){
869     Roo.bootstrap.Column.superclass.constructor.call(this, config);
870 };
871
872 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
873     
874     xs: false,
875     sm: false,
876     md: false,
877     lg: false,
878     xsoff: false,
879     smoff: false,
880     mdoff: false,
881     lgoff: false,
882     html: '',
883     offset: 0,
884     alert: false,
885     fa: false,
886     icon : false,
887     hidden : false,
888     fasize : 1,
889     
890     getAutoCreate : function(){
891         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
892         
893         cfg = {
894             tag: 'div',
895             cls: 'column'
896         };
897         
898         var settings=this;
899         ['xs','sm','md','lg'].map(function(size){
900             //Roo.log( size + ':' + settings[size]);
901             
902             if (settings[size+'off'] !== false) {
903                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
904             }
905             
906             if (settings[size] === false) {
907                 return;
908             }
909             Roo.log(settings[size]);
910             if (!settings[size]) { // 0 = hidden
911                 cfg.cls += ' hidden-' + size;
912                 return;
913             }
914             cfg.cls += ' col-' + size + '-' + settings[size];
915             
916         });
917         
918         if (this.hidden) {
919             cfg.cls += ' hidden';
920         }
921         
922         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
923             cfg.cls +=' alert alert-' + this.alert;
924         }
925         
926         
927         if (this.html.length) {
928             cfg.html = this.html;
929         }
930         if (this.fa) {
931             var fasize = '';
932             if (this.fasize > 1) {
933                 fasize = ' fa-' + this.fasize + 'x';
934             }
935             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
936             
937             
938         }
939         if (this.icon) {
940             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
941         }
942         
943         return cfg;
944     }
945    
946 });
947
948  
949
950  /*
951  * - LGPL
952  *
953  * page container.
954  * 
955  */
956
957
958 /**
959  * @class Roo.bootstrap.Container
960  * @extends Roo.bootstrap.Component
961  * Bootstrap Container class
962  * @cfg {Boolean} jumbotron is it a jumbotron element
963  * @cfg {String} html content of element
964  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
965  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
966  * @cfg {String} header content of header (for panel)
967  * @cfg {String} footer content of footer (for panel)
968  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
969  * @cfg {String} tag (header|aside|section) type of HTML tag.
970  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
971  * @cfg {String} fa (ban|check|...) font awesome icon
972  * @cfg {String} icon (info-sign|check|...) glyphicon name
973  * @cfg {Boolean} hidden (true|false) hide the element
974
975  *     
976  * @constructor
977  * Create a new Container
978  * @param {Object} config The config object
979  */
980
981 Roo.bootstrap.Container = function(config){
982     Roo.bootstrap.Container.superclass.constructor.call(this, config);
983 };
984
985 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
986     
987     jumbotron : false,
988     well: '',
989     panel : '',
990     header: '',
991     footer : '',
992     sticky: '',
993     tag : false,
994     alert : false,
995     fa: false,
996     icon : false,
997   
998      
999     getChildContainer : function() {
1000         
1001         if(!this.el){
1002             return false;
1003         }
1004         
1005         if (this.panel.length) {
1006             return this.el.select('.panel-body',true).first();
1007         }
1008         
1009         return this.el;
1010     },
1011     
1012     
1013     getAutoCreate : function(){
1014         
1015         var cfg = {
1016             tag : this.tag || 'div',
1017             html : '',
1018             cls : ''
1019         };
1020         if (this.jumbotron) {
1021             cfg.cls = 'jumbotron';
1022         }
1023         
1024         
1025         
1026         // - this is applied by the parent..
1027         //if (this.cls) {
1028         //    cfg.cls = this.cls + '';
1029         //}
1030         
1031         if (this.sticky.length) {
1032             
1033             var bd = Roo.get(document.body);
1034             if (!bd.hasClass('bootstrap-sticky')) {
1035                 bd.addClass('bootstrap-sticky');
1036                 Roo.select('html',true).setStyle('height', '100%');
1037             }
1038              
1039             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1040         }
1041         
1042         
1043         if (this.well.length) {
1044             switch (this.well) {
1045                 case 'lg':
1046                 case 'sm':
1047                     cfg.cls +=' well well-' +this.well;
1048                     break;
1049                 default:
1050                     cfg.cls +=' well';
1051                     break;
1052             }
1053         }
1054         
1055         if (this.hidden) {
1056             cfg.cls += ' hidden';
1057         }
1058         
1059         
1060         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1061             cfg.cls +=' alert alert-' + this.alert;
1062         }
1063         
1064         var body = cfg;
1065         
1066         if (this.panel.length) {
1067             cfg.cls += ' panel panel-' + this.panel;
1068             cfg.cn = [];
1069             if (this.header.length) {
1070                 cfg.cn.push({
1071                     
1072                     cls : 'panel-heading',
1073                     cn : [{
1074                         tag: 'h3',
1075                         cls : 'panel-title',
1076                         html : this.header
1077                     }]
1078                     
1079                 });
1080             }
1081             body = false;
1082             cfg.cn.push({
1083                 cls : 'panel-body',
1084                 html : this.html
1085             });
1086             
1087             
1088             if (this.footer.length) {
1089                 cfg.cn.push({
1090                     cls : 'panel-footer',
1091                     html : this.footer
1092                     
1093                 });
1094             }
1095             
1096         }
1097         
1098         if (body) {
1099             body.html = this.html || cfg.html;
1100             // prefix with the icons..
1101             if (this.fa) {
1102                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1103             }
1104             if (this.icon) {
1105                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1106             }
1107             
1108             
1109         }
1110         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1111             cfg.cls =  'container';
1112         }
1113         
1114         return cfg;
1115     },
1116     
1117     titleEl : function()
1118     {
1119         if(!this.el || !this.panel.length || !this.header.length){
1120             return;
1121         }
1122         
1123         return this.el.select('.panel-title',true).first();
1124     },
1125     
1126     setTitle : function(v)
1127     {
1128         var titleEl = this.titleEl();
1129         
1130         if(!titleEl){
1131             return;
1132         }
1133         
1134         titleEl.dom.innerHTML = v;
1135     },
1136     
1137     getTitle : function()
1138     {
1139         
1140         var titleEl = this.titleEl();
1141         
1142         if(!titleEl){
1143             return '';
1144         }
1145         
1146         return titleEl.dom.innerHTML;
1147     },
1148     
1149     show : function() {
1150         this.el.removeClass('hidden');
1151     },
1152     hide: function() {
1153         if (!this.el.hasClass('hidden')) {
1154             this.el.addClass('hidden');
1155         }
1156         
1157     }
1158    
1159 });
1160
1161  /*
1162  * - LGPL
1163  *
1164  * image
1165  * 
1166  */
1167
1168
1169 /**
1170  * @class Roo.bootstrap.Img
1171  * @extends Roo.bootstrap.Component
1172  * Bootstrap Img class
1173  * @cfg {Boolean} imgResponsive false | true
1174  * @cfg {String} border rounded | circle | thumbnail
1175  * @cfg {String} src image source
1176  * @cfg {String} alt image alternative text
1177  * @cfg {String} href a tag href
1178  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1179  * 
1180  * @constructor
1181  * Create a new Input
1182  * @param {Object} config The config object
1183  */
1184
1185 Roo.bootstrap.Img = function(config){
1186     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1187     
1188     this.addEvents({
1189         // img events
1190         /**
1191          * @event click
1192          * The img click event for the img.
1193          * @param {Roo.EventObject} e
1194          */
1195         "click" : true
1196     });
1197 };
1198
1199 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1200     
1201     imgResponsive: true,
1202     border: '',
1203     src: '',
1204     href: false,
1205     target: false,
1206
1207     getAutoCreate : function(){
1208         
1209         var cfg = {
1210             tag: 'img',
1211             cls: (this.imgResponsive) ? 'img-responsive' : '',
1212             html : null
1213         }
1214         
1215         cfg.html = this.html || cfg.html;
1216         
1217         cfg.src = this.src || cfg.src;
1218         
1219         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1220             cfg.cls += ' img-' + this.border;
1221         }
1222         
1223         if(this.alt){
1224             cfg.alt = this.alt;
1225         }
1226         
1227         if(this.href){
1228             var a = {
1229                 tag: 'a',
1230                 href: this.href,
1231                 cn: [
1232                     cfg
1233                 ]
1234             }
1235             
1236             if(this.target){
1237                 a.target = this.target;
1238             }
1239             
1240         }
1241         
1242         
1243         return (this.href) ? a : cfg;
1244     },
1245     
1246     initEvents: function() {
1247         
1248         if(!this.href){
1249             this.el.on('click', this.onClick, this);
1250         }
1251     },
1252     
1253     onClick : function(e)
1254     {
1255         Roo.log('img onclick');
1256         this.fireEvent('click', this, e);
1257     }
1258    
1259 });
1260
1261  /*
1262  * - LGPL
1263  *
1264  * image
1265  * 
1266  */
1267
1268
1269 /**
1270  * @class Roo.bootstrap.Link
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Link Class
1273  * @cfg {String} alt image alternative text
1274  * @cfg {String} href a tag href
1275  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1276  * @cfg {String} html the content of the link.
1277  * @cfg {String} anchor name for the anchor link
1278
1279  * @cfg {Boolean} preventDefault (true | false) default false
1280
1281  * 
1282  * @constructor
1283  * Create a new Input
1284  * @param {Object} config The config object
1285  */
1286
1287 Roo.bootstrap.Link = function(config){
1288     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1289     
1290     this.addEvents({
1291         // img events
1292         /**
1293          * @event click
1294          * The img click event for the img.
1295          * @param {Roo.EventObject} e
1296          */
1297         "click" : true
1298     });
1299 };
1300
1301 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1302     
1303     href: false,
1304     target: false,
1305     preventDefault: false,
1306     anchor : false,
1307     alt : false,
1308
1309     getAutoCreate : function()
1310     {
1311         
1312         var cfg = {
1313             tag: 'a'
1314         };
1315         // anchor's do not require html/href...
1316         if (this.anchor === false) {
1317             cfg.html = this.html || 'html-missing';
1318             cfg.href = this.href || '#';
1319         } else {
1320             cfg.name = this.anchor;
1321             if (this.html !== false) {
1322                 cfg.html = this.html;
1323             }
1324             if (this.href !== false) {
1325                 cfg.href = this.href;
1326             }
1327         }
1328         
1329         if(this.alt !== false){
1330             cfg.alt = this.alt;
1331         }
1332         
1333         
1334         if(this.target !== false) {
1335             cfg.target = this.target;
1336         }
1337         
1338         return cfg;
1339     },
1340     
1341     initEvents: function() {
1342         
1343         if(!this.href || this.preventDefault){
1344             this.el.on('click', this.onClick, this);
1345         }
1346     },
1347     
1348     onClick : function(e)
1349     {
1350         if(this.preventDefault){
1351             e.preventDefault();
1352         }
1353         //Roo.log('img onclick');
1354         this.fireEvent('click', this, e);
1355     }
1356    
1357 });
1358
1359  /*
1360  * - LGPL
1361  *
1362  * header
1363  * 
1364  */
1365
1366 /**
1367  * @class Roo.bootstrap.Header
1368  * @extends Roo.bootstrap.Component
1369  * Bootstrap Header class
1370  * @cfg {String} html content of header
1371  * @cfg {Number} level (1|2|3|4|5|6) default 1
1372  * 
1373  * @constructor
1374  * Create a new Header
1375  * @param {Object} config The config object
1376  */
1377
1378
1379 Roo.bootstrap.Header  = function(config){
1380     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1381 };
1382
1383 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1384     
1385     //href : false,
1386     html : false,
1387     level : 1,
1388     
1389     
1390     
1391     getAutoCreate : function(){
1392         
1393         
1394         
1395         var cfg = {
1396             tag: 'h' + (1 *this.level),
1397             html: this.html || ''
1398         } ;
1399         
1400         return cfg;
1401     }
1402    
1403 });
1404
1405  
1406
1407  /*
1408  * Based on:
1409  * Ext JS Library 1.1.1
1410  * Copyright(c) 2006-2007, Ext JS, LLC.
1411  *
1412  * Originally Released Under LGPL - original licence link has changed is not relivant.
1413  *
1414  * Fork - LGPL
1415  * <script type="text/javascript">
1416  */
1417  
1418 /**
1419  * @class Roo.bootstrap.MenuMgr
1420  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1421  * @singleton
1422  */
1423 Roo.bootstrap.MenuMgr = function(){
1424    var menus, active, groups = {}, attached = false, lastShow = new Date();
1425
1426    // private - called when first menu is created
1427    function init(){
1428        menus = {};
1429        active = new Roo.util.MixedCollection();
1430        Roo.get(document).addKeyListener(27, function(){
1431            if(active.length > 0){
1432                hideAll();
1433            }
1434        });
1435    }
1436
1437    // private
1438    function hideAll(){
1439        if(active && active.length > 0){
1440            var c = active.clone();
1441            c.each(function(m){
1442                m.hide();
1443            });
1444        }
1445    }
1446
1447    // private
1448    function onHide(m){
1449        active.remove(m);
1450        if(active.length < 1){
1451            Roo.get(document).un("mouseup", onMouseDown);
1452             
1453            attached = false;
1454        }
1455    }
1456
1457    // private
1458    function onShow(m){
1459        var last = active.last();
1460        lastShow = new Date();
1461        active.add(m);
1462        if(!attached){
1463           Roo.get(document).on("mouseup", onMouseDown);
1464            
1465            attached = true;
1466        }
1467        if(m.parentMenu){
1468           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1469           m.parentMenu.activeChild = m;
1470        }else if(last && last.isVisible()){
1471           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1472        }
1473    }
1474
1475    // private
1476    function onBeforeHide(m){
1477        if(m.activeChild){
1478            m.activeChild.hide();
1479        }
1480        if(m.autoHideTimer){
1481            clearTimeout(m.autoHideTimer);
1482            delete m.autoHideTimer;
1483        }
1484    }
1485
1486    // private
1487    function onBeforeShow(m){
1488        var pm = m.parentMenu;
1489        if(!pm && !m.allowOtherMenus){
1490            hideAll();
1491        }else if(pm && pm.activeChild && active != m){
1492            pm.activeChild.hide();
1493        }
1494    }
1495
1496    // private
1497    function onMouseDown(e){
1498         Roo.log("on MouseDown");
1499         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1500            hideAll();
1501         }
1502         
1503         
1504    }
1505
1506    // private
1507    function onBeforeCheck(mi, state){
1508        if(state){
1509            var g = groups[mi.group];
1510            for(var i = 0, l = g.length; i < l; i++){
1511                if(g[i] != mi){
1512                    g[i].setChecked(false);
1513                }
1514            }
1515        }
1516    }
1517
1518    return {
1519
1520        /**
1521         * Hides all menus that are currently visible
1522         */
1523        hideAll : function(){
1524             hideAll();  
1525        },
1526
1527        // private
1528        register : function(menu){
1529            if(!menus){
1530                init();
1531            }
1532            menus[menu.id] = menu;
1533            menu.on("beforehide", onBeforeHide);
1534            menu.on("hide", onHide);
1535            menu.on("beforeshow", onBeforeShow);
1536            menu.on("show", onShow);
1537            var g = menu.group;
1538            if(g && menu.events["checkchange"]){
1539                if(!groups[g]){
1540                    groups[g] = [];
1541                }
1542                groups[g].push(menu);
1543                menu.on("checkchange", onCheck);
1544            }
1545        },
1546
1547         /**
1548          * Returns a {@link Roo.menu.Menu} object
1549          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1550          * be used to generate and return a new Menu instance.
1551          */
1552        get : function(menu){
1553            if(typeof menu == "string"){ // menu id
1554                return menus[menu];
1555            }else if(menu.events){  // menu instance
1556                return menu;
1557            }
1558            /*else if(typeof menu.length == 'number'){ // array of menu items?
1559                return new Roo.bootstrap.Menu({items:menu});
1560            }else{ // otherwise, must be a config
1561                return new Roo.bootstrap.Menu(menu);
1562            }
1563            */
1564            return false;
1565        },
1566
1567        // private
1568        unregister : function(menu){
1569            delete menus[menu.id];
1570            menu.un("beforehide", onBeforeHide);
1571            menu.un("hide", onHide);
1572            menu.un("beforeshow", onBeforeShow);
1573            menu.un("show", onShow);
1574            var g = menu.group;
1575            if(g && menu.events["checkchange"]){
1576                groups[g].remove(menu);
1577                menu.un("checkchange", onCheck);
1578            }
1579        },
1580
1581        // private
1582        registerCheckable : function(menuItem){
1583            var g = menuItem.group;
1584            if(g){
1585                if(!groups[g]){
1586                    groups[g] = [];
1587                }
1588                groups[g].push(menuItem);
1589                menuItem.on("beforecheckchange", onBeforeCheck);
1590            }
1591        },
1592
1593        // private
1594        unregisterCheckable : function(menuItem){
1595            var g = menuItem.group;
1596            if(g){
1597                groups[g].remove(menuItem);
1598                menuItem.un("beforecheckchange", onBeforeCheck);
1599            }
1600        }
1601    };
1602 }();/*
1603  * - LGPL
1604  *
1605  * menu
1606  * 
1607  */
1608
1609 /**
1610  * @class Roo.bootstrap.Menu
1611  * @extends Roo.bootstrap.Component
1612  * Bootstrap Menu class - container for MenuItems
1613  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1614  * 
1615  * @constructor
1616  * Create a new Menu
1617  * @param {Object} config The config object
1618  */
1619
1620
1621 Roo.bootstrap.Menu = function(config){
1622     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1623     if (this.registerMenu) {
1624         Roo.bootstrap.MenuMgr.register(this);
1625     }
1626     this.addEvents({
1627         /**
1628          * @event beforeshow
1629          * Fires before this menu is displayed
1630          * @param {Roo.menu.Menu} this
1631          */
1632         beforeshow : true,
1633         /**
1634          * @event beforehide
1635          * Fires before this menu is hidden
1636          * @param {Roo.menu.Menu} this
1637          */
1638         beforehide : true,
1639         /**
1640          * @event show
1641          * Fires after this menu is displayed
1642          * @param {Roo.menu.Menu} this
1643          */
1644         show : true,
1645         /**
1646          * @event hide
1647          * Fires after this menu is hidden
1648          * @param {Roo.menu.Menu} this
1649          */
1650         hide : true,
1651         /**
1652          * @event click
1653          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1654          * @param {Roo.menu.Menu} this
1655          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1656          * @param {Roo.EventObject} e
1657          */
1658         click : true,
1659         /**
1660          * @event mouseover
1661          * Fires when the mouse is hovering over this menu
1662          * @param {Roo.menu.Menu} this
1663          * @param {Roo.EventObject} e
1664          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1665          */
1666         mouseover : true,
1667         /**
1668          * @event mouseout
1669          * Fires when the mouse exits this menu
1670          * @param {Roo.menu.Menu} this
1671          * @param {Roo.EventObject} e
1672          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1673          */
1674         mouseout : true,
1675         /**
1676          * @event itemclick
1677          * Fires when a menu item contained in this menu is clicked
1678          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1679          * @param {Roo.EventObject} e
1680          */
1681         itemclick: true
1682     });
1683     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1684 };
1685
1686 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1687     
1688    /// html : false,
1689     //align : '',
1690     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1691     type: false,
1692     /**
1693      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1694      */
1695     registerMenu : true,
1696     
1697     menuItems :false, // stores the menu items..
1698     
1699     hidden:true,
1700     
1701     parentMenu : false,
1702     
1703     getChildContainer : function() {
1704         return this.el;  
1705     },
1706     
1707     getAutoCreate : function(){
1708          
1709         //if (['right'].indexOf(this.align)!==-1) {
1710         //    cfg.cn[1].cls += ' pull-right'
1711         //}
1712         
1713         
1714         var cfg = {
1715             tag : 'ul',
1716             cls : 'dropdown-menu' ,
1717             style : 'z-index:1000'
1718             
1719         }
1720         
1721         if (this.type === 'submenu') {
1722             cfg.cls = 'submenu active';
1723         }
1724         if (this.type === 'treeview') {
1725             cfg.cls = 'treeview-menu';
1726         }
1727         
1728         return cfg;
1729     },
1730     initEvents : function() {
1731         
1732        // Roo.log("ADD event");
1733        // Roo.log(this.triggerEl.dom);
1734         this.triggerEl.on('click', this.onTriggerPress, this);
1735         this.triggerEl.addClass('dropdown-toggle');
1736         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1737
1738         this.el.on("mouseover", this.onMouseOver, this);
1739         this.el.on("mouseout", this.onMouseOut, this);
1740         
1741         
1742     },
1743     findTargetItem : function(e){
1744         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1745         if(!t){
1746             return false;
1747         }
1748         //Roo.log(t);         Roo.log(t.id);
1749         if(t && t.id){
1750             //Roo.log(this.menuitems);
1751             return this.menuitems.get(t.id);
1752             
1753             //return this.items.get(t.menuItemId);
1754         }
1755         
1756         return false;
1757     },
1758     onClick : function(e){
1759         Roo.log("menu.onClick");
1760         var t = this.findTargetItem(e);
1761         if(!t || t.isContainer){
1762             return;
1763         }
1764         Roo.log(e);
1765         /*
1766         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1767             if(t == this.activeItem && t.shouldDeactivate(e)){
1768                 this.activeItem.deactivate();
1769                 delete this.activeItem;
1770                 return;
1771             }
1772             if(t.canActivate){
1773                 this.setActiveItem(t, true);
1774             }
1775             return;
1776             
1777             
1778         }
1779         */
1780        
1781         Roo.log('pass click event');
1782         
1783         t.onClick(e);
1784         
1785         this.fireEvent("click", this, t, e);
1786         
1787         this.hide();
1788     },
1789      onMouseOver : function(e){
1790         var t  = this.findTargetItem(e);
1791         //Roo.log(t);
1792         //if(t){
1793         //    if(t.canActivate && !t.disabled){
1794         //        this.setActiveItem(t, true);
1795         //    }
1796         //}
1797         
1798         this.fireEvent("mouseover", this, e, t);
1799     },
1800     isVisible : function(){
1801         return !this.hidden;
1802     },
1803      onMouseOut : function(e){
1804         var t  = this.findTargetItem(e);
1805         
1806         //if(t ){
1807         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1808         //        this.activeItem.deactivate();
1809         //        delete this.activeItem;
1810         //    }
1811         //}
1812         this.fireEvent("mouseout", this, e, t);
1813     },
1814     
1815     
1816     /**
1817      * Displays this menu relative to another element
1818      * @param {String/HTMLElement/Roo.Element} element The element to align to
1819      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1820      * the element (defaults to this.defaultAlign)
1821      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1822      */
1823     show : function(el, pos, parentMenu){
1824         this.parentMenu = parentMenu;
1825         if(!this.el){
1826             this.render();
1827         }
1828         this.fireEvent("beforeshow", this);
1829         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1830     },
1831      /**
1832      * Displays this menu at a specific xy position
1833      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1834      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1835      */
1836     showAt : function(xy, parentMenu, /* private: */_e){
1837         this.parentMenu = parentMenu;
1838         if(!this.el){
1839             this.render();
1840         }
1841         if(_e !== false){
1842             this.fireEvent("beforeshow", this);
1843             //xy = this.el.adjustForConstraints(xy);
1844         }
1845         
1846         //this.el.show();
1847         this.hideMenuItems();
1848         this.hidden = false;
1849         this.triggerEl.addClass('open');
1850         
1851         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1852             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1853         }
1854         
1855         this.el.setXY(xy);
1856         this.focus();
1857         this.fireEvent("show", this);
1858     },
1859     
1860     focus : function(){
1861         return;
1862         if(!this.hidden){
1863             this.doFocus.defer(50, this);
1864         }
1865     },
1866
1867     doFocus : function(){
1868         if(!this.hidden){
1869             this.focusEl.focus();
1870         }
1871     },
1872
1873     /**
1874      * Hides this menu and optionally all parent menus
1875      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1876      */
1877     hide : function(deep){
1878         
1879         this.hideMenuItems();
1880         if(this.el && this.isVisible()){
1881             this.fireEvent("beforehide", this);
1882             if(this.activeItem){
1883                 this.activeItem.deactivate();
1884                 this.activeItem = null;
1885             }
1886             this.triggerEl.removeClass('open');;
1887             this.hidden = true;
1888             this.fireEvent("hide", this);
1889         }
1890         if(deep === true && this.parentMenu){
1891             this.parentMenu.hide(true);
1892         }
1893     },
1894     
1895     onTriggerPress  : function(e)
1896     {
1897         
1898         Roo.log('trigger press');
1899         //Roo.log(e.getTarget());
1900        // Roo.log(this.triggerEl.dom);
1901         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1902             return;
1903         }
1904         if (this.isVisible()) {
1905             Roo.log('hide');
1906             this.hide();
1907         } else {
1908             this.show(this.triggerEl, false, false);
1909         }
1910         
1911         
1912     },
1913     
1914          
1915        
1916     
1917     hideMenuItems : function()
1918     {
1919         //$(backdrop).remove()
1920         Roo.select('.open',true).each(function(aa) {
1921             
1922             aa.removeClass('open');
1923           //var parent = getParent($(this))
1924           //var relatedTarget = { relatedTarget: this }
1925           
1926            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1927           //if (e.isDefaultPrevented()) return
1928            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1929         })
1930     },
1931     addxtypeChild : function (tree, cntr) {
1932         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1933           
1934         this.menuitems.add(comp);
1935         return comp;
1936
1937     },
1938     getEl : function()
1939     {
1940         Roo.log(this.el);
1941         return this.el;
1942     }
1943 });
1944
1945  
1946  /*
1947  * - LGPL
1948  *
1949  * menu item
1950  * 
1951  */
1952
1953
1954 /**
1955  * @class Roo.bootstrap.MenuItem
1956  * @extends Roo.bootstrap.Component
1957  * Bootstrap MenuItem class
1958  * @cfg {String} html the menu label
1959  * @cfg {String} href the link
1960  * @cfg {Boolean} preventDefault (true | false) default true
1961  * @cfg {Boolean} isContainer (true | false) default false
1962  * 
1963  * 
1964  * @constructor
1965  * Create a new MenuItem
1966  * @param {Object} config The config object
1967  */
1968
1969
1970 Roo.bootstrap.MenuItem = function(config){
1971     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1972     this.addEvents({
1973         // raw events
1974         /**
1975          * @event click
1976          * The raw click event for the entire grid.
1977          * @param {Roo.bootstrap.MenuItem} this
1978          * @param {Roo.EventObject} e
1979          */
1980         "click" : true
1981     });
1982 };
1983
1984 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1985     
1986     href : false,
1987     html : false,
1988     preventDefault: true,
1989     isContainer : false,
1990     
1991     getAutoCreate : function(){
1992         
1993         if(this.isContainer){
1994             return {
1995                 tag: 'li',
1996                 cls: 'dropdown-menu-item'
1997             };
1998         }
1999         
2000         var cfg= {
2001             tag: 'li',
2002             cls: 'dropdown-menu-item',
2003             cn: [
2004                     {
2005                         tag : 'a',
2006                         href : '#',
2007                         html : 'Link'
2008                     }
2009                 ]
2010         };
2011         if (this.parent().type == 'treeview') {
2012             cfg.cls = 'treeview-menu';
2013         }
2014         
2015         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2016         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2017         return cfg;
2018     },
2019     
2020     initEvents: function() {
2021         
2022         //this.el.select('a').on('click', this.onClick, this);
2023         
2024     },
2025     onClick : function(e)
2026     {
2027         Roo.log('item on click ');
2028         //if(this.preventDefault){
2029         //    e.preventDefault();
2030         //}
2031         //this.parent().hideMenuItems();
2032         
2033         this.fireEvent('click', this, e);
2034     },
2035     getEl : function()
2036     {
2037         return this.el;
2038     }
2039 });
2040
2041  
2042
2043  /*
2044  * - LGPL
2045  *
2046  * menu separator
2047  * 
2048  */
2049
2050
2051 /**
2052  * @class Roo.bootstrap.MenuSeparator
2053  * @extends Roo.bootstrap.Component
2054  * Bootstrap MenuSeparator class
2055  * 
2056  * @constructor
2057  * Create a new MenuItem
2058  * @param {Object} config The config object
2059  */
2060
2061
2062 Roo.bootstrap.MenuSeparator = function(config){
2063     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2064 };
2065
2066 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2067     
2068     getAutoCreate : function(){
2069         var cfg = {
2070             cls: 'divider',
2071             tag : 'li'
2072         };
2073         
2074         return cfg;
2075     }
2076    
2077 });
2078
2079  
2080
2081  
2082 /*
2083 * Licence: LGPL
2084 */
2085
2086 /**
2087  * @class Roo.bootstrap.Modal
2088  * @extends Roo.bootstrap.Component
2089  * Bootstrap Modal class
2090  * @cfg {String} title Title of dialog
2091  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2092  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2093  * @cfg {Boolean} specificTitle default false
2094  * @cfg {Array} buttons Array of buttons or standard button set..
2095  * @cfg {String} buttonPosition (left|right|center) default right
2096  * @cfg {Boolean} animate default true
2097  * @cfg {Boolean} allow_close default true
2098  * 
2099  * @constructor
2100  * Create a new Modal Dialog
2101  * @param {Object} config The config object
2102  */
2103
2104 Roo.bootstrap.Modal = function(config){
2105     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2106     this.addEvents({
2107         // raw events
2108         /**
2109          * @event btnclick
2110          * The raw btnclick event for the button
2111          * @param {Roo.EventObject} e
2112          */
2113         "btnclick" : true
2114     });
2115     this.buttons = this.buttons || [];
2116      
2117     if (this.tmpl) {
2118         this.tmpl = Roo.factory(this.tmpl);
2119     }
2120     
2121 };
2122
2123 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2124     
2125     title : 'test dialog',
2126    
2127     buttons : false,
2128     
2129     // set on load...
2130      
2131     html: false,
2132     
2133     tmp: false,
2134     
2135     specificTitle: false,
2136     
2137     buttonPosition: 'right',
2138     
2139     allow_close : true,
2140     
2141     animate : true,
2142     
2143     
2144      // private
2145     bodyEl:  false,
2146     footerEl:  false,
2147     titleEl:  false,
2148     closeEl:  false,
2149     
2150     
2151     onRender : function(ct, position)
2152     {
2153         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2154      
2155         if(!this.el){
2156             var cfg = Roo.apply({},  this.getAutoCreate());
2157             cfg.id = Roo.id();
2158             //if(!cfg.name){
2159             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2160             //}
2161             //if (!cfg.name.length) {
2162             //    delete cfg.name;
2163            // }
2164             if (this.cls) {
2165                 cfg.cls += ' ' + this.cls;
2166             }
2167             if (this.style) {
2168                 cfg.style = this.style;
2169             }
2170             this.el = Roo.get(document.body).createChild(cfg, position);
2171         }
2172         //var type = this.el.dom.type;
2173         
2174         
2175         
2176         
2177         if(this.tabIndex !== undefined){
2178             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2179         }
2180         
2181         
2182         this.bodyEl = this.el.select('.modal-body',true).first();
2183         this.closeEl = this.el.select('.modal-header .close', true).first();
2184         this.footerEl = this.el.select('.modal-footer',true).first();
2185         this.titleEl = this.el.select('.modal-title',true).first();
2186         
2187         
2188          
2189         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2190         this.maskEl.enableDisplayMode("block");
2191         this.maskEl.hide();
2192         //this.el.addClass("x-dlg-modal");
2193     
2194         if (this.buttons.length) {
2195             Roo.each(this.buttons, function(bb) {
2196                 b = Roo.apply({}, bb);
2197                 b.xns = b.xns || Roo.bootstrap;
2198                 b.xtype = b.xtype || 'Button';
2199                 if (typeof(b.listeners) == 'undefined') {
2200                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2201                 }
2202                 
2203                 var btn = Roo.factory(b);
2204                 
2205                 btn.onRender(this.el.select('.modal-footer div').first());
2206                 
2207             },this);
2208         }
2209         // render the children.
2210         var nitems = [];
2211         
2212         if(typeof(this.items) != 'undefined'){
2213             var items = this.items;
2214             delete this.items;
2215
2216             for(var i =0;i < items.length;i++) {
2217                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2218             }
2219         }
2220         
2221         this.items = nitems;
2222         
2223         // where are these used - they used to be body/close/footer
2224         
2225        
2226         this.initEvents();
2227         //this.el.addClass([this.fieldClass, this.cls]);
2228         
2229     },
2230     getAutoCreate : function(){
2231         
2232         
2233         var bdy = {
2234                 cls : 'modal-body',
2235                 html : this.html || ''
2236         };
2237         
2238         var title = {
2239             tag: 'h4',
2240             cls : 'modal-title',
2241             html : this.title
2242         };
2243         
2244         if(this.specificTitle){
2245             title = this.title;
2246             
2247         };
2248         
2249         var header = [];
2250         if (this.allow_close) {
2251             header.push({
2252                 tag: 'button',
2253                 cls : 'close',
2254                 html : '&times'
2255             });
2256         }
2257         header.push(title);
2258         
2259         var modal = {
2260             cls: "modal",
2261             style : 'display: none',
2262             cn : [
2263                 {
2264                     cls: "modal-dialog",
2265                     cn : [
2266                         {
2267                             cls : "modal-content",
2268                             cn : [
2269                                 {
2270                                     cls : 'modal-header',
2271                                     cn : header
2272                                 },
2273                                 bdy,
2274                                 {
2275                                     cls : 'modal-footer',
2276                                     cn : [
2277                                         {
2278                                             tag: 'div',
2279                                             cls: 'btn-' + this.buttonPosition
2280                                         }
2281                                     ]
2282                                     
2283                                 }
2284                                 
2285                                 
2286                             ]
2287                             
2288                         }
2289                     ]
2290                         
2291                 }
2292             ]
2293         };
2294         
2295         if(this.animate){
2296             modal.cls += ' fade';
2297         }
2298         
2299         return modal;
2300           
2301     },
2302     getChildContainer : function() {
2303          
2304          return this.bodyEl;
2305         
2306     },
2307     getButtonContainer : function() {
2308          return this.el.select('.modal-footer div',true).first();
2309         
2310     },
2311     initEvents : function()
2312     {
2313         if (this.allow_close) {
2314             this.closeEl.on('click', this.hide, this);
2315         }
2316
2317     },
2318     show : function() {
2319         
2320         if (!this.rendered) {
2321             this.render();
2322         }
2323         
2324         this.el.setStyle('display', 'block');
2325         
2326         if(this.animate){
2327             var _this = this;
2328             (function(){ _this.el.addClass('in'); }).defer(50);
2329         }else{
2330             this.el.addClass('in');
2331         }
2332         
2333         // not sure how we can show data in here.. 
2334         //if (this.tmpl) {
2335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2336         //}
2337         
2338         Roo.get(document.body).addClass("x-body-masked");
2339         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2340         this.maskEl.show();
2341         this.el.setStyle('zIndex', '10001');
2342        
2343         this.fireEvent('show', this);
2344         
2345         
2346     },
2347     hide : function()
2348     {
2349         this.maskEl.hide();
2350         Roo.get(document.body).removeClass("x-body-masked");
2351         this.el.removeClass('in');
2352         
2353         if(this.animate){
2354             var _this = this;
2355             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2356         }else{
2357             this.el.setStyle('display', 'none');
2358         }
2359         
2360         this.fireEvent('hide', this);
2361     },
2362     
2363     addButton : function(str, cb)
2364     {
2365          
2366         
2367         var b = Roo.apply({}, { html : str } );
2368         b.xns = b.xns || Roo.bootstrap;
2369         b.xtype = b.xtype || 'Button';
2370         if (typeof(b.listeners) == 'undefined') {
2371             b.listeners = { click : cb.createDelegate(this)  };
2372         }
2373         
2374         var btn = Roo.factory(b);
2375            
2376         btn.onRender(this.el.select('.modal-footer div').first());
2377         
2378         return btn;   
2379        
2380     },
2381     
2382     setDefaultButton : function(btn)
2383     {
2384         //this.el.select('.modal-footer').()
2385     },
2386     resizeTo: function(w,h)
2387     {
2388         // skip..
2389     },
2390     setContentSize  : function(w, h)
2391     {
2392         
2393     },
2394     onButtonClick: function(btn,e)
2395     {
2396         //Roo.log([a,b,c]);
2397         this.fireEvent('btnclick', btn.name, e);
2398     },
2399      /**
2400      * Set the title of the Dialog
2401      * @param {String} str new Title
2402      */
2403     setTitle: function(str) {
2404         this.titleEl.dom.innerHTML = str;    
2405     },
2406     /**
2407      * Set the body of the Dialog
2408      * @param {String} str new Title
2409      */
2410     setBody: function(str) {
2411         this.bodyEl.dom.innerHTML = str;    
2412     },
2413     /**
2414      * Set the body of the Dialog using the template
2415      * @param {Obj} data - apply this data to the template and replace the body contents.
2416      */
2417     applyBody: function(obj)
2418     {
2419         if (!this.tmpl) {
2420             Roo.log("Error - using apply Body without a template");
2421             //code
2422         }
2423         this.tmpl.overwrite(this.bodyEl, obj);
2424     }
2425     
2426 });
2427
2428
2429 Roo.apply(Roo.bootstrap.Modal,  {
2430     /**
2431          * Button config that displays a single OK button
2432          * @type Object
2433          */
2434         OK :  [{
2435             name : 'ok',
2436             weight : 'primary',
2437             html : 'OK'
2438         }], 
2439         /**
2440          * Button config that displays Yes and No buttons
2441          * @type Object
2442          */
2443         YESNO : [
2444             {
2445                 name  : 'no',
2446                 html : 'No'
2447             },
2448             {
2449                 name  :'yes',
2450                 weight : 'primary',
2451                 html : 'Yes'
2452             }
2453         ],
2454         
2455         /**
2456          * Button config that displays OK and Cancel buttons
2457          * @type Object
2458          */
2459         OKCANCEL : [
2460             {
2461                name : 'cancel',
2462                 html : 'Cancel'
2463             },
2464             {
2465                 name : 'ok',
2466                 weight : 'primary',
2467                 html : 'OK'
2468             }
2469         ],
2470         /**
2471          * Button config that displays Yes, No and Cancel buttons
2472          * @type Object
2473          */
2474         YESNOCANCEL : [
2475             {
2476                 name : 'yes',
2477                 weight : 'primary',
2478                 html : 'Yes'
2479             },
2480             {
2481                 name : 'no',
2482                 html : 'No'
2483             },
2484             {
2485                 name : 'cancel',
2486                 html : 'Cancel'
2487             }
2488         ]
2489 });
2490  
2491  /*
2492  * - LGPL
2493  *
2494  * messagebox - can be used as a replace
2495  * 
2496  */
2497 /**
2498  * @class Roo.MessageBox
2499  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2500  * Example usage:
2501  *<pre><code>
2502 // Basic alert:
2503 Roo.Msg.alert('Status', 'Changes saved successfully.');
2504
2505 // Prompt for user data:
2506 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2507     if (btn == 'ok'){
2508         // process text value...
2509     }
2510 });
2511
2512 // Show a dialog using config options:
2513 Roo.Msg.show({
2514    title:'Save Changes?',
2515    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2516    buttons: Roo.Msg.YESNOCANCEL,
2517    fn: processResult,
2518    animEl: 'elId'
2519 });
2520 </code></pre>
2521  * @singleton
2522  */
2523 Roo.bootstrap.MessageBox = function(){
2524     var dlg, opt, mask, waitTimer;
2525     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2526     var buttons, activeTextEl, bwidth;
2527
2528     
2529     // private
2530     var handleButton = function(button){
2531         dlg.hide();
2532         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2533     };
2534
2535     // private
2536     var handleHide = function(){
2537         if(opt && opt.cls){
2538             dlg.el.removeClass(opt.cls);
2539         }
2540         //if(waitTimer){
2541         //    Roo.TaskMgr.stop(waitTimer);
2542         //    waitTimer = null;
2543         //}
2544     };
2545
2546     // private
2547     var updateButtons = function(b){
2548         var width = 0;
2549         if(!b){
2550             buttons["ok"].hide();
2551             buttons["cancel"].hide();
2552             buttons["yes"].hide();
2553             buttons["no"].hide();
2554             //dlg.footer.dom.style.display = 'none';
2555             return width;
2556         }
2557         dlg.footerEl.dom.style.display = '';
2558         for(var k in buttons){
2559             if(typeof buttons[k] != "function"){
2560                 if(b[k]){
2561                     buttons[k].show();
2562                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2563                     width += buttons[k].el.getWidth()+15;
2564                 }else{
2565                     buttons[k].hide();
2566                 }
2567             }
2568         }
2569         return width;
2570     };
2571
2572     // private
2573     var handleEsc = function(d, k, e){
2574         if(opt && opt.closable !== false){
2575             dlg.hide();
2576         }
2577         if(e){
2578             e.stopEvent();
2579         }
2580     };
2581
2582     return {
2583         /**
2584          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2585          * @return {Roo.BasicDialog} The BasicDialog element
2586          */
2587         getDialog : function(){
2588            if(!dlg){
2589                 dlg = new Roo.bootstrap.Modal( {
2590                     //draggable: true,
2591                     //resizable:false,
2592                     //constraintoviewport:false,
2593                     //fixedcenter:true,
2594                     //collapsible : false,
2595                     //shim:true,
2596                     //modal: true,
2597                   //  width:400,
2598                   //  height:100,
2599                     //buttonAlign:"center",
2600                     closeClick : function(){
2601                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2602                             handleButton("no");
2603                         }else{
2604                             handleButton("cancel");
2605                         }
2606                     }
2607                 });
2608                 dlg.render();
2609                 dlg.on("hide", handleHide);
2610                 mask = dlg.mask;
2611                 //dlg.addKeyListener(27, handleEsc);
2612                 buttons = {};
2613                 this.buttons = buttons;
2614                 var bt = this.buttonText;
2615                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2616                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2617                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2618                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2619                 Roo.log(buttons)
2620                 bodyEl = dlg.bodyEl.createChild({
2621
2622                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2623                         '<textarea class="roo-mb-textarea"></textarea>' +
2624                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2625                 });
2626                 msgEl = bodyEl.dom.firstChild;
2627                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2628                 textboxEl.enableDisplayMode();
2629                 textboxEl.addKeyListener([10,13], function(){
2630                     if(dlg.isVisible() && opt && opt.buttons){
2631                         if(opt.buttons.ok){
2632                             handleButton("ok");
2633                         }else if(opt.buttons.yes){
2634                             handleButton("yes");
2635                         }
2636                     }
2637                 });
2638                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2639                 textareaEl.enableDisplayMode();
2640                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2641                 progressEl.enableDisplayMode();
2642                 var pf = progressEl.dom.firstChild;
2643                 if (pf) {
2644                     pp = Roo.get(pf.firstChild);
2645                     pp.setHeight(pf.offsetHeight);
2646                 }
2647                 
2648             }
2649             return dlg;
2650         },
2651
2652         /**
2653          * Updates the message box body text
2654          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2655          * the XHTML-compliant non-breaking space character '&amp;#160;')
2656          * @return {Roo.MessageBox} This message box
2657          */
2658         updateText : function(text){
2659             if(!dlg.isVisible() && !opt.width){
2660                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2661             }
2662             msgEl.innerHTML = text || '&#160;';
2663       
2664             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2665             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2666             var w = Math.max(
2667                     Math.min(opt.width || cw , this.maxWidth), 
2668                     Math.max(opt.minWidth || this.minWidth, bwidth)
2669             );
2670             if(opt.prompt){
2671                 activeTextEl.setWidth(w);
2672             }
2673             if(dlg.isVisible()){
2674                 dlg.fixedcenter = false;
2675             }
2676             // to big, make it scroll. = But as usual stupid IE does not support
2677             // !important..
2678             
2679             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2680                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2681                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2682             } else {
2683                 bodyEl.dom.style.height = '';
2684                 bodyEl.dom.style.overflowY = '';
2685             }
2686             if (cw > w) {
2687                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2688             } else {
2689                 bodyEl.dom.style.overflowX = '';
2690             }
2691             
2692             dlg.setContentSize(w, bodyEl.getHeight());
2693             if(dlg.isVisible()){
2694                 dlg.fixedcenter = true;
2695             }
2696             return this;
2697         },
2698
2699         /**
2700          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2701          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2702          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2703          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2704          * @return {Roo.MessageBox} This message box
2705          */
2706         updateProgress : function(value, text){
2707             if(text){
2708                 this.updateText(text);
2709             }
2710             if (pp) { // weird bug on my firefox - for some reason this is not defined
2711                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2712             }
2713             return this;
2714         },        
2715
2716         /**
2717          * Returns true if the message box is currently displayed
2718          * @return {Boolean} True if the message box is visible, else false
2719          */
2720         isVisible : function(){
2721             return dlg && dlg.isVisible();  
2722         },
2723
2724         /**
2725          * Hides the message box if it is displayed
2726          */
2727         hide : function(){
2728             if(this.isVisible()){
2729                 dlg.hide();
2730             }  
2731         },
2732
2733         /**
2734          * Displays a new message box, or reinitializes an existing message box, based on the config options
2735          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2736          * The following config object properties are supported:
2737          * <pre>
2738 Property    Type             Description
2739 ----------  ---------------  ------------------------------------------------------------------------------------
2740 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2741                                    closes (defaults to undefined)
2742 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2743                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2744 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2745                                    progress and wait dialogs will ignore this property and always hide the
2746                                    close button as they can only be closed programmatically.
2747 cls               String           A custom CSS class to apply to the message box element
2748 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2749                                    displayed (defaults to 75)
2750 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2751                                    function will be btn (the name of the button that was clicked, if applicable,
2752                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2753                                    Progress and wait dialogs will ignore this option since they do not respond to
2754                                    user actions and can only be closed programmatically, so any required function
2755                                    should be called by the same code after it closes the dialog.
2756 icon              String           A CSS class that provides a background image to be used as an icon for
2757                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2758 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2759 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2760 modal             Boolean          False to allow user interaction with the page while the message box is
2761                                    displayed (defaults to true)
2762 msg               String           A string that will replace the existing message box body text (defaults
2763                                    to the XHTML-compliant non-breaking space character '&#160;')
2764 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2765 progress          Boolean          True to display a progress bar (defaults to false)
2766 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2767 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2768 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2769 title             String           The title text
2770 value             String           The string value to set into the active textbox element if displayed
2771 wait              Boolean          True to display a progress bar (defaults to false)
2772 width             Number           The width of the dialog in pixels
2773 </pre>
2774          *
2775          * Example usage:
2776          * <pre><code>
2777 Roo.Msg.show({
2778    title: 'Address',
2779    msg: 'Please enter your address:',
2780    width: 300,
2781    buttons: Roo.MessageBox.OKCANCEL,
2782    multiline: true,
2783    fn: saveAddress,
2784    animEl: 'addAddressBtn'
2785 });
2786 </code></pre>
2787          * @param {Object} config Configuration options
2788          * @return {Roo.MessageBox} This message box
2789          */
2790         show : function(options)
2791         {
2792             
2793             // this causes nightmares if you show one dialog after another
2794             // especially on callbacks..
2795              
2796             if(this.isVisible()){
2797                 
2798                 this.hide();
2799                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2800                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2801                 Roo.log("New Dialog Message:" +  options.msg )
2802                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2803                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2804                 
2805             }
2806             var d = this.getDialog();
2807             opt = options;
2808             d.setTitle(opt.title || "&#160;");
2809             d.closeEl.setDisplayed(opt.closable !== false);
2810             activeTextEl = textboxEl;
2811             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2812             if(opt.prompt){
2813                 if(opt.multiline){
2814                     textboxEl.hide();
2815                     textareaEl.show();
2816                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2817                         opt.multiline : this.defaultTextHeight);
2818                     activeTextEl = textareaEl;
2819                 }else{
2820                     textboxEl.show();
2821                     textareaEl.hide();
2822                 }
2823             }else{
2824                 textboxEl.hide();
2825                 textareaEl.hide();
2826             }
2827             progressEl.setDisplayed(opt.progress === true);
2828             this.updateProgress(0);
2829             activeTextEl.dom.value = opt.value || "";
2830             if(opt.prompt){
2831                 dlg.setDefaultButton(activeTextEl);
2832             }else{
2833                 var bs = opt.buttons;
2834                 var db = null;
2835                 if(bs && bs.ok){
2836                     db = buttons["ok"];
2837                 }else if(bs && bs.yes){
2838                     db = buttons["yes"];
2839                 }
2840                 dlg.setDefaultButton(db);
2841             }
2842             bwidth = updateButtons(opt.buttons);
2843             this.updateText(opt.msg);
2844             if(opt.cls){
2845                 d.el.addClass(opt.cls);
2846             }
2847             d.proxyDrag = opt.proxyDrag === true;
2848             d.modal = opt.modal !== false;
2849             d.mask = opt.modal !== false ? mask : false;
2850             if(!d.isVisible()){
2851                 // force it to the end of the z-index stack so it gets a cursor in FF
2852                 document.body.appendChild(dlg.el.dom);
2853                 d.animateTarget = null;
2854                 d.show(options.animEl);
2855             }
2856             return this;
2857         },
2858
2859         /**
2860          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2861          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2862          * and closing the message box when the process is complete.
2863          * @param {String} title The title bar text
2864          * @param {String} msg The message box body text
2865          * @return {Roo.MessageBox} This message box
2866          */
2867         progress : function(title, msg){
2868             this.show({
2869                 title : title,
2870                 msg : msg,
2871                 buttons: false,
2872                 progress:true,
2873                 closable:false,
2874                 minWidth: this.minProgressWidth,
2875                 modal : true
2876             });
2877             return this;
2878         },
2879
2880         /**
2881          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2882          * If a callback function is passed it will be called after the user clicks the button, and the
2883          * id of the button that was clicked will be passed as the only parameter to the callback
2884          * (could also be the top-right close button).
2885          * @param {String} title The title bar text
2886          * @param {String} msg The message box body text
2887          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2888          * @param {Object} scope (optional) The scope of the callback function
2889          * @return {Roo.MessageBox} This message box
2890          */
2891         alert : function(title, msg, fn, scope){
2892             this.show({
2893                 title : title,
2894                 msg : msg,
2895                 buttons: this.OK,
2896                 fn: fn,
2897                 scope : scope,
2898                 modal : true
2899             });
2900             return this;
2901         },
2902
2903         /**
2904          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2905          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2906          * You are responsible for closing the message box when the process is complete.
2907          * @param {String} msg The message box body text
2908          * @param {String} title (optional) The title bar text
2909          * @return {Roo.MessageBox} This message box
2910          */
2911         wait : function(msg, title){
2912             this.show({
2913                 title : title,
2914                 msg : msg,
2915                 buttons: false,
2916                 closable:false,
2917                 progress:true,
2918                 modal:true,
2919                 width:300,
2920                 wait:true
2921             });
2922             waitTimer = Roo.TaskMgr.start({
2923                 run: function(i){
2924                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2925                 },
2926                 interval: 1000
2927             });
2928             return this;
2929         },
2930
2931         /**
2932          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2933          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2934          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2935          * @param {String} title The title bar text
2936          * @param {String} msg The message box body text
2937          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2938          * @param {Object} scope (optional) The scope of the callback function
2939          * @return {Roo.MessageBox} This message box
2940          */
2941         confirm : function(title, msg, fn, scope){
2942             this.show({
2943                 title : title,
2944                 msg : msg,
2945                 buttons: this.YESNO,
2946                 fn: fn,
2947                 scope : scope,
2948                 modal : true
2949             });
2950             return this;
2951         },
2952
2953         /**
2954          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2955          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2956          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2957          * (could also be the top-right close button) and the text that was entered will be passed as the two
2958          * parameters to the callback.
2959          * @param {String} title The title bar text
2960          * @param {String} msg The message box body text
2961          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2962          * @param {Object} scope (optional) The scope of the callback function
2963          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2964          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2965          * @return {Roo.MessageBox} This message box
2966          */
2967         prompt : function(title, msg, fn, scope, multiline){
2968             this.show({
2969                 title : title,
2970                 msg : msg,
2971                 buttons: this.OKCANCEL,
2972                 fn: fn,
2973                 minWidth:250,
2974                 scope : scope,
2975                 prompt:true,
2976                 multiline: multiline,
2977                 modal : true
2978             });
2979             return this;
2980         },
2981
2982         /**
2983          * Button config that displays a single OK button
2984          * @type Object
2985          */
2986         OK : {ok:true},
2987         /**
2988          * Button config that displays Yes and No buttons
2989          * @type Object
2990          */
2991         YESNO : {yes:true, no:true},
2992         /**
2993          * Button config that displays OK and Cancel buttons
2994          * @type Object
2995          */
2996         OKCANCEL : {ok:true, cancel:true},
2997         /**
2998          * Button config that displays Yes, No and Cancel buttons
2999          * @type Object
3000          */
3001         YESNOCANCEL : {yes:true, no:true, cancel:true},
3002
3003         /**
3004          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3005          * @type Number
3006          */
3007         defaultTextHeight : 75,
3008         /**
3009          * The maximum width in pixels of the message box (defaults to 600)
3010          * @type Number
3011          */
3012         maxWidth : 600,
3013         /**
3014          * The minimum width in pixels of the message box (defaults to 100)
3015          * @type Number
3016          */
3017         minWidth : 100,
3018         /**
3019          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3020          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3021          * @type Number
3022          */
3023         minProgressWidth : 250,
3024         /**
3025          * An object containing the default button text strings that can be overriden for localized language support.
3026          * Supported properties are: ok, cancel, yes and no.
3027          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3028          * @type Object
3029          */
3030         buttonText : {
3031             ok : "OK",
3032             cancel : "Cancel",
3033             yes : "Yes",
3034             no : "No"
3035         }
3036     };
3037 }();
3038
3039 /**
3040  * Shorthand for {@link Roo.MessageBox}
3041  */
3042 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3043 Roo.Msg = Roo.Msg || Roo.MessageBox;
3044 /*
3045  * - LGPL
3046  *
3047  * navbar
3048  * 
3049  */
3050
3051 /**
3052  * @class Roo.bootstrap.Navbar
3053  * @extends Roo.bootstrap.Component
3054  * Bootstrap Navbar class
3055
3056  * @constructor
3057  * Create a new Navbar
3058  * @param {Object} config The config object
3059  */
3060
3061
3062 Roo.bootstrap.Navbar = function(config){
3063     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3064     
3065 };
3066
3067 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3068     
3069     
3070    
3071     // private
3072     navItems : false,
3073     loadMask : false,
3074     
3075     
3076     getAutoCreate : function(){
3077         
3078         
3079         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3080         
3081     },
3082     
3083     initEvents :function ()
3084     {
3085         //Roo.log(this.el.select('.navbar-toggle',true));
3086         this.el.select('.navbar-toggle',true).on('click', function() {
3087            // Roo.log('click');
3088             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3089         }, this);
3090         
3091         var mark = {
3092             tag: "div",
3093             cls:"x-dlg-mask"
3094         }
3095         
3096         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3097         
3098         var size = this.el.getSize();
3099         this.maskEl.setSize(size.width, size.height);
3100         this.maskEl.enableDisplayMode("block");
3101         this.maskEl.hide();
3102         
3103         if(this.loadMask){
3104             this.maskEl.show();
3105         }
3106     },
3107     
3108     
3109     getChildContainer : function()
3110     {
3111         if (this.el.select('.collapse').getCount()) {
3112             return this.el.select('.collapse',true).first();
3113         }
3114         
3115         return this.el;
3116     },
3117     
3118     mask : function()
3119     {
3120         this.maskEl.show();
3121     },
3122     
3123     unmask : function()
3124     {
3125         this.maskEl.hide();
3126     } 
3127     
3128     
3129     
3130     
3131 });
3132
3133
3134
3135  
3136
3137  /*
3138  * - LGPL
3139  *
3140  * navbar
3141  * 
3142  */
3143
3144 /**
3145  * @class Roo.bootstrap.NavSimplebar
3146  * @extends Roo.bootstrap.Navbar
3147  * Bootstrap Sidebar class
3148  *
3149  * @cfg {Boolean} inverse is inverted color
3150  * 
3151  * @cfg {String} type (nav | pills | tabs)
3152  * @cfg {Boolean} arrangement stacked | justified
3153  * @cfg {String} align (left | right) alignment
3154  * 
3155  * @cfg {Boolean} main (true|false) main nav bar? default false
3156  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3157  * 
3158  * @cfg {String} tag (header|footer|nav|div) default is nav 
3159
3160  * 
3161  * 
3162  * 
3163  * @constructor
3164  * Create a new Sidebar
3165  * @param {Object} config The config object
3166  */
3167
3168
3169 Roo.bootstrap.NavSimplebar = function(config){
3170     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3171 };
3172
3173 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3174     
3175     inverse: false,
3176     
3177     type: false,
3178     arrangement: '',
3179     align : false,
3180     
3181     
3182     
3183     main : false,
3184     
3185     
3186     tag : false,
3187     
3188     
3189     getAutoCreate : function(){
3190         
3191         
3192         var cfg = {
3193             tag : this.tag || 'div',
3194             cls : 'navbar'
3195         };
3196           
3197         
3198         cfg.cn = [
3199             {
3200                 cls: 'nav',
3201                 tag : 'ul'
3202             }
3203         ];
3204         
3205          
3206         this.type = this.type || 'nav';
3207         if (['tabs','pills'].indexOf(this.type)!==-1) {
3208             cfg.cn[0].cls += ' nav-' + this.type
3209         
3210         
3211         } else {
3212             if (this.type!=='nav') {
3213                 Roo.log('nav type must be nav/tabs/pills')
3214             }
3215             cfg.cn[0].cls += ' navbar-nav'
3216         }
3217         
3218         
3219         
3220         
3221         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3222             cfg.cn[0].cls += ' nav-' + this.arrangement;
3223         }
3224         
3225         
3226         if (this.align === 'right') {
3227             cfg.cn[0].cls += ' navbar-right';
3228         }
3229         
3230         if (this.inverse) {
3231             cfg.cls += ' navbar-inverse';
3232             
3233         }
3234         
3235         
3236         return cfg;
3237     
3238         
3239     }
3240     
3241     
3242     
3243 });
3244
3245
3246
3247  
3248
3249  
3250        /*
3251  * - LGPL
3252  *
3253  * navbar
3254  * 
3255  */
3256
3257 /**
3258  * @class Roo.bootstrap.NavHeaderbar
3259  * @extends Roo.bootstrap.NavSimplebar
3260  * Bootstrap Sidebar class
3261  *
3262  * @cfg {String} brand what is brand
3263  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3264  * @cfg {String} brand_href href of the brand
3265  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3266  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3267  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3268  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3269  * 
3270  * @constructor
3271  * Create a new Sidebar
3272  * @param {Object} config The config object
3273  */
3274
3275
3276 Roo.bootstrap.NavHeaderbar = function(config){
3277     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3278       
3279 };
3280
3281 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3282     
3283     position: '',
3284     brand: '',
3285     brand_href: false,
3286     srButton : true,
3287     autohide : false,
3288     desktopCenter : false,
3289    
3290     
3291     getAutoCreate : function(){
3292         
3293         var   cfg = {
3294             tag: this.nav || 'nav',
3295             cls: 'navbar',
3296             role: 'navigation',
3297             cn: []
3298         };
3299         
3300         var cn = cfg.cn;
3301         if (this.desktopCenter) {
3302             cn.push({cls : 'container', cn : []});
3303             cn = cn[0].cn;
3304         }
3305         
3306         if(this.srButton){
3307             cn.push({
3308                 tag: 'div',
3309                 cls: 'navbar-header',
3310                 cn: [
3311                     {
3312                         tag: 'button',
3313                         type: 'button',
3314                         cls: 'navbar-toggle',
3315                         'data-toggle': 'collapse',
3316                         cn: [
3317                             {
3318                                 tag: 'span',
3319                                 cls: 'sr-only',
3320                                 html: 'Toggle navigation'
3321                             },
3322                             {
3323                                 tag: 'span',
3324                                 cls: 'icon-bar'
3325                             },
3326                             {
3327                                 tag: 'span',
3328                                 cls: 'icon-bar'
3329                             },
3330                             {
3331                                 tag: 'span',
3332                                 cls: 'icon-bar'
3333                             }
3334                         ]
3335                     }
3336                 ]
3337             });
3338         }
3339         
3340         cn.push({
3341             tag: 'div',
3342             cls: 'collapse navbar-collapse',
3343             cn : []
3344         });
3345         
3346         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3347         
3348         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3349             cfg.cls += ' navbar-' + this.position;
3350             
3351             // tag can override this..
3352             
3353             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3354         }
3355         
3356         if (this.brand !== '') {
3357             cn[0].cn.push({
3358                 tag: 'a',
3359                 href: this.brand_href ? this.brand_href : '#',
3360                 cls: 'navbar-brand',
3361                 cn: [
3362                 this.brand
3363                 ]
3364             });
3365         }
3366         
3367         if(this.main){
3368             cfg.cls += ' main-nav';
3369         }
3370         
3371         
3372         return cfg;
3373
3374         
3375     },
3376     getHeaderChildContainer : function()
3377     {
3378         if (this.el.select('.navbar-header').getCount()) {
3379             return this.el.select('.navbar-header',true).first();
3380         }
3381         
3382         return this.getChildContainer();
3383     },
3384     
3385     
3386     initEvents : function()
3387     {
3388         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3389         
3390         if (this.autohide) {
3391             
3392             var prevScroll = 0;
3393             var ft = this.el;
3394             
3395             Roo.get(document).on('scroll',function(e) {
3396                 var ns = Roo.get(document).getScroll().top;
3397                 var os = prevScroll;
3398                 prevScroll = ns;
3399                 
3400                 if(ns > os){
3401                     ft.removeClass('slideDown');
3402                     ft.addClass('slideUp');
3403                     return;
3404                 }
3405                 ft.removeClass('slideUp');
3406                 ft.addClass('slideDown');
3407                  
3408               
3409           },this);
3410         }
3411     }    
3412     
3413 });
3414
3415
3416
3417  
3418
3419  /*
3420  * - LGPL
3421  *
3422  * navbar
3423  * 
3424  */
3425
3426 /**
3427  * @class Roo.bootstrap.NavSidebar
3428  * @extends Roo.bootstrap.Navbar
3429  * Bootstrap Sidebar class
3430  * 
3431  * @constructor
3432  * Create a new Sidebar
3433  * @param {Object} config The config object
3434  */
3435
3436
3437 Roo.bootstrap.NavSidebar = function(config){
3438     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3439 };
3440
3441 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3442     
3443     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3444     
3445     getAutoCreate : function(){
3446         
3447         
3448         return  {
3449             tag: 'div',
3450             cls: 'sidebar sidebar-nav'
3451         };
3452     
3453         
3454     }
3455     
3456     
3457     
3458 });
3459
3460
3461
3462  
3463
3464  /*
3465  * - LGPL
3466  *
3467  * nav group
3468  * 
3469  */
3470
3471 /**
3472  * @class Roo.bootstrap.NavGroup
3473  * @extends Roo.bootstrap.Component
3474  * Bootstrap NavGroup class
3475  * @cfg {String} align left | right
3476  * @cfg {Boolean} inverse false | true
3477  * @cfg {String} type (nav|pills|tab) default nav
3478  * @cfg {String} navId - reference Id for navbar.
3479
3480  * 
3481  * @constructor
3482  * Create a new nav group
3483  * @param {Object} config The config object
3484  */
3485
3486 Roo.bootstrap.NavGroup = function(config){
3487     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3488     this.navItems = [];
3489    
3490     Roo.bootstrap.NavGroup.register(this);
3491      this.addEvents({
3492         /**
3493              * @event changed
3494              * Fires when the active item changes
3495              * @param {Roo.bootstrap.NavGroup} this
3496              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3497              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3498          */
3499         'changed': true
3500      });
3501     
3502 };
3503
3504 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3505     
3506     align: '',
3507     inverse: false,
3508     form: false,
3509     type: 'nav',
3510     navId : '',
3511     // private
3512     
3513     navItems : false, 
3514     
3515     getAutoCreate : function()
3516     {
3517         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3518         
3519         cfg = {
3520             tag : 'ul',
3521             cls: 'nav' 
3522         }
3523         
3524         if (['tabs','pills'].indexOf(this.type)!==-1) {
3525             cfg.cls += ' nav-' + this.type
3526         } else {
3527             if (this.type!=='nav') {
3528                 Roo.log('nav type must be nav/tabs/pills')
3529             }
3530             cfg.cls += ' navbar-nav'
3531         }
3532         
3533         if (this.parent().sidebar) {
3534             cfg = {
3535                 tag: 'ul',
3536                 cls: 'dashboard-menu sidebar-menu'
3537             }
3538             
3539             return cfg;
3540         }
3541         
3542         if (this.form === true) {
3543             cfg = {
3544                 tag: 'form',
3545                 cls: 'navbar-form'
3546             }
3547             
3548             if (this.align === 'right') {
3549                 cfg.cls += ' navbar-right';
3550             } else {
3551                 cfg.cls += ' navbar-left';
3552             }
3553         }
3554         
3555         if (this.align === 'right') {
3556             cfg.cls += ' navbar-right';
3557         }
3558         
3559         if (this.inverse) {
3560             cfg.cls += ' navbar-inverse';
3561             
3562         }
3563         
3564         
3565         return cfg;
3566     },
3567     /**
3568     * sets the active Navigation item
3569     * @param {Roo.bootstrap.NavItem} the new current navitem
3570     */
3571     setActiveItem : function(item)
3572     {
3573         var prev = false;
3574         Roo.each(this.navItems, function(v){
3575             if (v == item) {
3576                 return ;
3577             }
3578             if (v.isActive()) {
3579                 v.setActive(false, true);
3580                 prev = v;
3581                 
3582             }
3583             
3584         });
3585
3586         item.setActive(true, true);
3587         this.fireEvent('changed', this, item, prev);
3588         
3589         
3590     },
3591     /**
3592     * gets the active Navigation item
3593     * @return {Roo.bootstrap.NavItem} the current navitem
3594     */
3595     getActive : function()
3596     {
3597         
3598         var prev = false;
3599         Roo.each(this.navItems, function(v){
3600             
3601             if (v.isActive()) {
3602                 prev = v;
3603                 
3604             }
3605             
3606         });
3607         return prev;
3608     },
3609     
3610     indexOfNav : function()
3611     {
3612         
3613         var prev = false;
3614         Roo.each(this.navItems, function(v,i){
3615             
3616             if (v.isActive()) {
3617                 prev = i;
3618                 
3619             }
3620             
3621         });
3622         return prev;
3623     },
3624     /**
3625     * adds a Navigation item
3626     * @param {Roo.bootstrap.NavItem} the navitem to add
3627     */
3628     addItem : function(cfg)
3629     {
3630         var cn = new Roo.bootstrap.NavItem(cfg);
3631         this.register(cn);
3632         cn.parentId = this.id;
3633         cn.onRender(this.el, null);
3634         return cn;
3635     },
3636     /**
3637     * register a Navigation item
3638     * @param {Roo.bootstrap.NavItem} the navitem to add
3639     */
3640     register : function(item)
3641     {
3642         this.navItems.push( item);
3643         item.navId = this.navId;
3644     
3645     },
3646     
3647     /**
3648     * clear all the Navigation item
3649     */
3650    
3651     clearAll : function()
3652     {
3653         this.navItems = [];
3654         this.el.dom.innerHTML = '';
3655     },
3656     
3657     getNavItem: function(tabId)
3658     {
3659         var ret = false;
3660         Roo.each(this.navItems, function(e) {
3661             if (e.tabId == tabId) {
3662                ret =  e;
3663                return false;
3664             }
3665             return true;
3666             
3667         });
3668         return ret;
3669     },
3670     
3671     setActiveNext : function()
3672     {
3673         var i = this.indexOfNav(this.getActive());
3674         if (i > this.navItems.length) {
3675             return;
3676         }
3677         this.setActiveItem(this.navItems[i+1]);
3678     },
3679     setActivePrev : function()
3680     {
3681         var i = this.indexOfNav(this.getActive());
3682         if (i  < 1) {
3683             return;
3684         }
3685         this.setActiveItem(this.navItems[i-1]);
3686     },
3687     clearWasActive : function(except) {
3688         Roo.each(this.navItems, function(e) {
3689             if (e.tabId != except.tabId && e.was_active) {
3690                e.was_active = false;
3691                return false;
3692             }
3693             return true;
3694             
3695         });
3696     },
3697     getWasActive : function ()
3698     {
3699         var r = false;
3700         Roo.each(this.navItems, function(e) {
3701             if (e.was_active) {
3702                r = e;
3703                return false;
3704             }
3705             return true;
3706             
3707         });
3708         return r;
3709     }
3710     
3711     
3712 });
3713
3714  
3715 Roo.apply(Roo.bootstrap.NavGroup, {
3716     
3717     groups: {},
3718      /**
3719     * register a Navigation Group
3720     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3721     */
3722     register : function(navgrp)
3723     {
3724         this.groups[navgrp.navId] = navgrp;
3725         
3726     },
3727     /**
3728     * fetch a Navigation Group based on the navigation ID
3729     * @param {string} the navgroup to add
3730     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3731     */
3732     get: function(navId) {
3733         if (typeof(this.groups[navId]) == 'undefined') {
3734             return false;
3735             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3736         }
3737         return this.groups[navId] ;
3738     }
3739     
3740     
3741     
3742 });
3743
3744  /*
3745  * - LGPL
3746  *
3747  * row
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavItem
3753  * @extends Roo.bootstrap.Component
3754  * Bootstrap Navbar.NavItem class
3755  * @cfg {String} href  link to
3756  * @cfg {String} html content of button
3757  * @cfg {String} badge text inside badge
3758  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3759  * @cfg {String} glyphicon name of glyphicon
3760  * @cfg {String} icon name of font awesome icon
3761  * @cfg {Boolean} active Is item active
3762  * @cfg {Boolean} disabled Is item disabled
3763  
3764  * @cfg {Boolean} preventDefault (true | false) default false
3765  * @cfg {String} tabId the tab that this item activates.
3766  * @cfg {String} tagtype (a|span) render as a href or span?
3767  * @cfg {Boolean} animateRef (true|false) link to element default false  
3768   
3769  * @constructor
3770  * Create a new Navbar Item
3771  * @param {Object} config The config object
3772  */
3773 Roo.bootstrap.NavItem = function(config){
3774     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3775     this.addEvents({
3776         // raw events
3777         /**
3778          * @event click
3779          * The raw click event for the entire grid.
3780          * @param {Roo.EventObject} e
3781          */
3782         "click" : true,
3783          /**
3784             * @event changed
3785             * Fires when the active item active state changes
3786             * @param {Roo.bootstrap.NavItem} this
3787             * @param {boolean} state the new state
3788              
3789          */
3790         'changed': true,
3791         /**
3792             * @event scrollto
3793             * Fires when scroll to element
3794             * @param {Roo.bootstrap.NavItem} this
3795             * @param {Object} options
3796             * @param {Roo.EventObject} e
3797              
3798          */
3799         'scrollto': true
3800     });
3801    
3802 };
3803
3804 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3805     
3806     href: false,
3807     html: '',
3808     badge: '',
3809     icon: false,
3810     glyphicon: false,
3811     active: false,
3812     preventDefault : false,
3813     tabId : false,
3814     tagtype : 'a',
3815     disabled : false,
3816     animateRef : false,
3817     was_active : false,
3818     
3819     getAutoCreate : function(){
3820          
3821         var cfg = {
3822             tag: 'li',
3823             cls: 'nav-item'
3824             
3825         }
3826         if (this.active) {
3827             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3828         }
3829         if (this.disabled) {
3830             cfg.cls += ' disabled';
3831         }
3832         
3833         if (this.href || this.html || this.glyphicon || this.icon) {
3834             cfg.cn = [
3835                 {
3836                     tag: this.tagtype,
3837                     href : this.href || "#",
3838                     html: this.html || ''
3839                 }
3840             ];
3841             
3842             if (this.icon) {
3843                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3844             }
3845
3846             if(this.glyphicon) {
3847                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3848             }
3849             
3850             if (this.menu) {
3851                 
3852                 cfg.cn[0].html += " <span class='caret'></span>";
3853              
3854             }
3855             
3856             if (this.badge !== '') {
3857                  
3858                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3859             }
3860         }
3861         
3862         
3863         
3864         return cfg;
3865     },
3866     initEvents: function() 
3867     {
3868         if (typeof (this.menu) != 'undefined') {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874         this.el.select('a',true).on('click', this.onClick, this);
3875         
3876         if(this.tagtype == 'span'){
3877             this.el.select('span',true).on('click', this.onClick, this);
3878         }
3879        
3880         // at this point parent should be available..
3881         this.parent().register(this);
3882     },
3883     
3884     onClick : function(e)
3885     {
3886         if(
3887                 this.preventDefault || 
3888                 this.href == '#' 
3889         ){
3890             
3891             e.preventDefault();
3892         }
3893         
3894         if (this.disabled) {
3895             return;
3896         }
3897         
3898         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3899         if (tg && tg.transition) {
3900             Roo.log("waiting for the transitionend");
3901             return;
3902         }
3903         
3904         
3905         
3906         //Roo.log("fire event clicked");
3907         if(this.fireEvent('click', this, e) === false){
3908             return;
3909         };
3910         
3911         if(this.tagtype == 'span'){
3912             return;
3913         }
3914         
3915         //Roo.log(this.href);
3916         var ael = this.el.select('a',true).first();
3917         //Roo.log(ael);
3918         
3919         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3920             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3921             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3922                 return; // ignore... - it's a 'hash' to another page.
3923             }
3924             
3925             e.preventDefault();
3926             this.scrollToElement(e);
3927         }
3928         
3929         
3930         var p =  this.parent();
3931    
3932         if (['tabs','pills'].indexOf(p.type)!==-1) {
3933             if (typeof(p.setActiveItem) !== 'undefined') {
3934                 p.setActiveItem(this);
3935             }
3936         }
3937     },
3938     
3939     isActive: function () {
3940         return this.active
3941     },
3942     setActive : function(state, fire, is_was_active)
3943     {
3944         if (this.active && !state & this.navId) {
3945             this.was_active = true;
3946             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3947             if (nv) {
3948                 nv.clearWasActive(this);
3949             }
3950             
3951         }
3952         this.active = state;
3953         
3954         if (!state ) {
3955             this.el.removeClass('active');
3956         } else if (!this.el.hasClass('active')) {
3957             this.el.addClass('active');
3958         }
3959         if (fire) {
3960             this.fireEvent('changed', this, state);
3961         }
3962         
3963         // show a panel if it's registered and related..
3964         
3965         if (!this.navId || !this.tabId || !state || is_was_active) {
3966             return;
3967         }
3968         
3969         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3970         if (!tg) {
3971             return;
3972         }
3973         var pan = tg.getPanelByName(this.tabId);
3974         if (!pan) {
3975             return;
3976         }
3977         // if we can not flip to new panel - go back to old nav highlight..
3978         if (false == tg.showPanel(pan)) {
3979             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3980             if (nv) {
3981                 var onav = nv.getWasActive();
3982                 if (onav) {
3983                     onav.setActive(true, false, true);
3984                 }
3985             }
3986             
3987         }
3988         
3989         
3990         
3991     },
3992      // this should not be here...
3993     setDisabled : function(state)
3994     {
3995         this.disabled = state;
3996         if (!state ) {
3997             this.el.removeClass('disabled');
3998         } else if (!this.el.hasClass('disabled')) {
3999             this.el.addClass('disabled');
4000         }
4001         
4002     },
4003     
4004     /**
4005      * Fetch the element to display the tooltip on.
4006      * @return {Roo.Element} defaults to this.el
4007      */
4008     tooltipEl : function()
4009     {
4010         return this.el.select('' + this.tagtype + '', true).first();
4011     },
4012     
4013     scrollToElement : function(e)
4014     {
4015         var c = document.body;
4016         
4017         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4018         
4019         if(!target){
4020             return;
4021         }
4022
4023         var o = target.calcOffsetsTo(c);
4024         
4025         var options = {
4026             target : target,
4027             value : o[1]
4028         }
4029         
4030         this.fireEvent('scrollto', this, options, e);
4031         
4032         Roo.get(c).scrollTo('top', options.value, true);
4033         
4034         return;
4035     }
4036 });
4037  
4038
4039  /*
4040  * - LGPL
4041  *
4042  * sidebar item
4043  *
4044  *  li
4045  *    <span> icon </span>
4046  *    <span> text </span>
4047  *    <span>badge </span>
4048  */
4049
4050 /**
4051  * @class Roo.bootstrap.NavSidebarItem
4052  * @extends Roo.bootstrap.NavItem
4053  * Bootstrap Navbar.NavSidebarItem class
4054  * @constructor
4055  * Create a new Navbar Button
4056  * @param {Object} config The config object
4057  */
4058 Roo.bootstrap.NavSidebarItem = function(config){
4059     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4060     this.addEvents({
4061         // raw events
4062         /**
4063          * @event click
4064          * The raw click event for the entire grid.
4065          * @param {Roo.EventObject} e
4066          */
4067         "click" : true,
4068          /**
4069             * @event changed
4070             * Fires when the active item active state changes
4071             * @param {Roo.bootstrap.NavSidebarItem} this
4072             * @param {boolean} state the new state
4073              
4074          */
4075         'changed': true
4076     });
4077    
4078 };
4079
4080 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4081     
4082     
4083     getAutoCreate : function(){
4084         
4085         
4086         var a = {
4087                 tag: 'a',
4088                 href : this.href || '#',
4089                 cls: '',
4090                 html : '',
4091                 cn : []
4092         };
4093         var cfg = {
4094             tag: 'li',
4095             cls: '',
4096             cn: [ a ]
4097         }
4098         var span = {
4099             tag: 'span',
4100             html : this.html || ''
4101         }
4102         
4103         
4104         if (this.active) {
4105             cfg.cls += ' active';
4106         }
4107         
4108         // left icon..
4109         if (this.glyphicon || this.icon) {
4110             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4111             a.cn.push({ tag : 'i', cls : c }) ;
4112         }
4113         // html..
4114         a.cn.push(span);
4115         // then badge..
4116         if (this.badge !== '') {
4117             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4118         }
4119         // fi
4120         if (this.menu) {
4121             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4122             a.cls += 'dropdown-toggle treeview' ;
4123             
4124         }
4125         
4126         
4127         
4128         return cfg;
4129          
4130            
4131     }
4132    
4133      
4134  
4135 });
4136  
4137
4138  /*
4139  * - LGPL
4140  *
4141  * row
4142  * 
4143  */
4144
4145 /**
4146  * @class Roo.bootstrap.Row
4147  * @extends Roo.bootstrap.Component
4148  * Bootstrap Row class (contains columns...)
4149  * 
4150  * @constructor
4151  * Create a new Row
4152  * @param {Object} config The config object
4153  */
4154
4155 Roo.bootstrap.Row = function(config){
4156     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4157 };
4158
4159 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4160     
4161     getAutoCreate : function(){
4162        return {
4163             cls: 'row clearfix'
4164        };
4165     }
4166     
4167     
4168 });
4169
4170  
4171
4172  /*
4173  * - LGPL
4174  *
4175  * element
4176  * 
4177  */
4178
4179 /**
4180  * @class Roo.bootstrap.Element
4181  * @extends Roo.bootstrap.Component
4182  * Bootstrap Element class
4183  * @cfg {String} html contents of the element
4184  * @cfg {String} tag tag of the element
4185  * @cfg {String} cls class of the element
4186  * @cfg {Boolean} preventDefault (true|false) default false
4187  * @cfg {Boolean} clickable (true|false) default false
4188  * 
4189  * @constructor
4190  * Create a new Element
4191  * @param {Object} config The config object
4192  */
4193
4194 Roo.bootstrap.Element = function(config){
4195     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4196     
4197     this.addEvents({
4198         // raw events
4199         /**
4200          * @event click
4201          * When a element is chick
4202          * @param {Roo.bootstrap.Element} this
4203          * @param {Roo.EventObject} e
4204          */
4205         "click" : true
4206     });
4207 };
4208
4209 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4210     
4211     tag: 'div',
4212     cls: '',
4213     html: '',
4214     preventDefault: false, 
4215     clickable: false,
4216     
4217     getAutoCreate : function(){
4218         
4219         var cfg = {
4220             tag: this.tag,
4221             cls: this.cls,
4222             html: this.html
4223         }
4224         
4225         return cfg;
4226     },
4227     
4228     initEvents: function() 
4229     {
4230         Roo.bootstrap.Element.superclass.initEvents.call(this);
4231         
4232         if(this.clickable){
4233             this.el.on('click', this.onClick, this);
4234         }
4235         
4236     },
4237     
4238     onClick : function(e)
4239     {
4240         if(this.preventDefault){
4241             e.preventDefault();
4242         }
4243         
4244         this.fireEvent('click', this, e);
4245     },
4246     
4247     getValue : function()
4248     {
4249         return this.el.dom.innerHTML;
4250     },
4251     
4252     setValue : function(value)
4253     {
4254         this.el.dom.innerHTML = value;
4255     }
4256    
4257 });
4258
4259  
4260
4261  /*
4262  * - LGPL
4263  *
4264  * pagination
4265  * 
4266  */
4267
4268 /**
4269  * @class Roo.bootstrap.Pagination
4270  * @extends Roo.bootstrap.Component
4271  * Bootstrap Pagination class
4272  * @cfg {String} size xs | sm | md | lg
4273  * @cfg {Boolean} inverse false | true
4274  * 
4275  * @constructor
4276  * Create a new Pagination
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Pagination = function(config){
4281     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4282 };
4283
4284 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4285     
4286     cls: false,
4287     size: false,
4288     inverse: false,
4289     
4290     getAutoCreate : function(){
4291         var cfg = {
4292             tag: 'ul',
4293                 cls: 'pagination'
4294         };
4295         if (this.inverse) {
4296             cfg.cls += ' inverse';
4297         }
4298         if (this.html) {
4299             cfg.html=this.html;
4300         }
4301         if (this.cls) {
4302             cfg.cls += " " + this.cls;
4303         }
4304         return cfg;
4305     }
4306    
4307 });
4308
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * Pagination item
4315  * 
4316  */
4317
4318
4319 /**
4320  * @class Roo.bootstrap.PaginationItem
4321  * @extends Roo.bootstrap.Component
4322  * Bootstrap PaginationItem class
4323  * @cfg {String} html text
4324  * @cfg {String} href the link
4325  * @cfg {Boolean} preventDefault (true | false) default true
4326  * @cfg {Boolean} active (true | false) default false
4327  * @cfg {Boolean} disabled default false
4328  * 
4329  * 
4330  * @constructor
4331  * Create a new PaginationItem
4332  * @param {Object} config The config object
4333  */
4334
4335
4336 Roo.bootstrap.PaginationItem = function(config){
4337     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4338     this.addEvents({
4339         // raw events
4340         /**
4341          * @event click
4342          * The raw click event for the entire grid.
4343          * @param {Roo.EventObject} e
4344          */
4345         "click" : true
4346     });
4347 };
4348
4349 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4350     
4351     href : false,
4352     html : false,
4353     preventDefault: true,
4354     active : false,
4355     cls : false,
4356     disabled: false,
4357     
4358     getAutoCreate : function(){
4359         var cfg= {
4360             tag: 'li',
4361             cn: [
4362                 {
4363                     tag : 'a',
4364                     href : this.href ? this.href : '#',
4365                     html : this.html ? this.html : ''
4366                 }
4367             ]
4368         };
4369         
4370         if(this.cls){
4371             cfg.cls = this.cls;
4372         }
4373         
4374         if(this.disabled){
4375             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4376         }
4377         
4378         if(this.active){
4379             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4380         }
4381         
4382         return cfg;
4383     },
4384     
4385     initEvents: function() {
4386         
4387         this.el.on('click', this.onClick, this);
4388         
4389     },
4390     onClick : function(e)
4391     {
4392         Roo.log('PaginationItem on click ');
4393         if(this.preventDefault){
4394             e.preventDefault();
4395         }
4396         
4397         if(this.disabled){
4398             return;
4399         }
4400         
4401         this.fireEvent('click', this, e);
4402     }
4403    
4404 });
4405
4406  
4407
4408  /*
4409  * - LGPL
4410  *
4411  * slider
4412  * 
4413  */
4414
4415
4416 /**
4417  * @class Roo.bootstrap.Slider
4418  * @extends Roo.bootstrap.Component
4419  * Bootstrap Slider class
4420  *    
4421  * @constructor
4422  * Create a new Slider
4423  * @param {Object} config The config object
4424  */
4425
4426 Roo.bootstrap.Slider = function(config){
4427     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4428 };
4429
4430 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4431     
4432     getAutoCreate : function(){
4433         
4434         var cfg = {
4435             tag: 'div',
4436             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4437             cn: [
4438                 {
4439                     tag: 'a',
4440                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4441                 }
4442             ]
4443         }
4444         
4445         return cfg;
4446     }
4447    
4448 });
4449
4450  /*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460  
4461
4462 /**
4463  * @class Roo.grid.ColumnModel
4464  * @extends Roo.util.Observable
4465  * This is the default implementation of a ColumnModel used by the Grid. It defines
4466  * the columns in the grid.
4467  * <br>Usage:<br>
4468  <pre><code>
4469  var colModel = new Roo.grid.ColumnModel([
4470         {header: "Ticker", width: 60, sortable: true, locked: true},
4471         {header: "Company Name", width: 150, sortable: true},
4472         {header: "Market Cap.", width: 100, sortable: true},
4473         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4474         {header: "Employees", width: 100, sortable: true, resizable: false}
4475  ]);
4476  </code></pre>
4477  * <p>
4478  
4479  * The config options listed for this class are options which may appear in each
4480  * individual column definition.
4481  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4482  * @constructor
4483  * @param {Object} config An Array of column config objects. See this class's
4484  * config objects for details.
4485 */
4486 Roo.grid.ColumnModel = function(config){
4487         /**
4488      * The config passed into the constructor
4489      */
4490     this.config = config;
4491     this.lookup = {};
4492
4493     // if no id, create one
4494     // if the column does not have a dataIndex mapping,
4495     // map it to the order it is in the config
4496     for(var i = 0, len = config.length; i < len; i++){
4497         var c = config[i];
4498         if(typeof c.dataIndex == "undefined"){
4499             c.dataIndex = i;
4500         }
4501         if(typeof c.renderer == "string"){
4502             c.renderer = Roo.util.Format[c.renderer];
4503         }
4504         if(typeof c.id == "undefined"){
4505             c.id = Roo.id();
4506         }
4507         if(c.editor && c.editor.xtype){
4508             c.editor  = Roo.factory(c.editor, Roo.grid);
4509         }
4510         if(c.editor && c.editor.isFormField){
4511             c.editor = new Roo.grid.GridEditor(c.editor);
4512         }
4513         this.lookup[c.id] = c;
4514     }
4515
4516     /**
4517      * The width of columns which have no width specified (defaults to 100)
4518      * @type Number
4519      */
4520     this.defaultWidth = 100;
4521
4522     /**
4523      * Default sortable of columns which have no sortable specified (defaults to false)
4524      * @type Boolean
4525      */
4526     this.defaultSortable = false;
4527
4528     this.addEvents({
4529         /**
4530              * @event widthchange
4531              * Fires when the width of a column changes.
4532              * @param {ColumnModel} this
4533              * @param {Number} columnIndex The column index
4534              * @param {Number} newWidth The new width
4535              */
4536             "widthchange": true,
4537         /**
4538              * @event headerchange
4539              * Fires when the text of a header changes.
4540              * @param {ColumnModel} this
4541              * @param {Number} columnIndex The column index
4542              * @param {Number} newText The new header text
4543              */
4544             "headerchange": true,
4545         /**
4546              * @event hiddenchange
4547              * Fires when a column is hidden or "unhidden".
4548              * @param {ColumnModel} this
4549              * @param {Number} columnIndex The column index
4550              * @param {Boolean} hidden true if hidden, false otherwise
4551              */
4552             "hiddenchange": true,
4553             /**
4554          * @event columnmoved
4555          * Fires when a column is moved.
4556          * @param {ColumnModel} this
4557          * @param {Number} oldIndex
4558          * @param {Number} newIndex
4559          */
4560         "columnmoved" : true,
4561         /**
4562          * @event columlockchange
4563          * Fires when a column's locked state is changed
4564          * @param {ColumnModel} this
4565          * @param {Number} colIndex
4566          * @param {Boolean} locked true if locked
4567          */
4568         "columnlockchange" : true
4569     });
4570     Roo.grid.ColumnModel.superclass.constructor.call(this);
4571 };
4572 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4573     /**
4574      * @cfg {String} header The header text to display in the Grid view.
4575      */
4576     /**
4577      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4578      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4579      * specified, the column's index is used as an index into the Record's data Array.
4580      */
4581     /**
4582      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4583      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4584      */
4585     /**
4586      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4587      * Defaults to the value of the {@link #defaultSortable} property.
4588      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4589      */
4590     /**
4591      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4592      */
4593     /**
4594      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4595      */
4596     /**
4597      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4598      */
4599     /**
4600      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4601      */
4602     /**
4603      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4604      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4605      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4606      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4607      */
4608        /**
4609      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4610      */
4611     /**
4612      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4613      */
4614     /**
4615      * @cfg {String} cursor (Optional)
4616      */
4617     /**
4618      * @cfg {String} tooltip (Optional)
4619      */
4620     /**
4621      * Returns the id of the column at the specified index.
4622      * @param {Number} index The column index
4623      * @return {String} the id
4624      */
4625     getColumnId : function(index){
4626         return this.config[index].id;
4627     },
4628
4629     /**
4630      * Returns the column for a specified id.
4631      * @param {String} id The column id
4632      * @return {Object} the column
4633      */
4634     getColumnById : function(id){
4635         return this.lookup[id];
4636     },
4637
4638     
4639     /**
4640      * Returns the column for a specified dataIndex.
4641      * @param {String} dataIndex The column dataIndex
4642      * @return {Object|Boolean} the column or false if not found
4643      */
4644     getColumnByDataIndex: function(dataIndex){
4645         var index = this.findColumnIndex(dataIndex);
4646         return index > -1 ? this.config[index] : false;
4647     },
4648     
4649     /**
4650      * Returns the index for a specified column id.
4651      * @param {String} id The column id
4652      * @return {Number} the index, or -1 if not found
4653      */
4654     getIndexById : function(id){
4655         for(var i = 0, len = this.config.length; i < len; i++){
4656             if(this.config[i].id == id){
4657                 return i;
4658             }
4659         }
4660         return -1;
4661     },
4662     
4663     /**
4664      * Returns the index for a specified column dataIndex.
4665      * @param {String} dataIndex The column dataIndex
4666      * @return {Number} the index, or -1 if not found
4667      */
4668     
4669     findColumnIndex : function(dataIndex){
4670         for(var i = 0, len = this.config.length; i < len; i++){
4671             if(this.config[i].dataIndex == dataIndex){
4672                 return i;
4673             }
4674         }
4675         return -1;
4676     },
4677     
4678     
4679     moveColumn : function(oldIndex, newIndex){
4680         var c = this.config[oldIndex];
4681         this.config.splice(oldIndex, 1);
4682         this.config.splice(newIndex, 0, c);
4683         this.dataMap = null;
4684         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4685     },
4686
4687     isLocked : function(colIndex){
4688         return this.config[colIndex].locked === true;
4689     },
4690
4691     setLocked : function(colIndex, value, suppressEvent){
4692         if(this.isLocked(colIndex) == value){
4693             return;
4694         }
4695         this.config[colIndex].locked = value;
4696         if(!suppressEvent){
4697             this.fireEvent("columnlockchange", this, colIndex, value);
4698         }
4699     },
4700
4701     getTotalLockedWidth : function(){
4702         var totalWidth = 0;
4703         for(var i = 0; i < this.config.length; i++){
4704             if(this.isLocked(i) && !this.isHidden(i)){
4705                 this.totalWidth += this.getColumnWidth(i);
4706             }
4707         }
4708         return totalWidth;
4709     },
4710
4711     getLockedCount : function(){
4712         for(var i = 0, len = this.config.length; i < len; i++){
4713             if(!this.isLocked(i)){
4714                 return i;
4715             }
4716         }
4717     },
4718
4719     /**
4720      * Returns the number of columns.
4721      * @return {Number}
4722      */
4723     getColumnCount : function(visibleOnly){
4724         if(visibleOnly === true){
4725             var c = 0;
4726             for(var i = 0, len = this.config.length; i < len; i++){
4727                 if(!this.isHidden(i)){
4728                     c++;
4729                 }
4730             }
4731             return c;
4732         }
4733         return this.config.length;
4734     },
4735
4736     /**
4737      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4738      * @param {Function} fn
4739      * @param {Object} scope (optional)
4740      * @return {Array} result
4741      */
4742     getColumnsBy : function(fn, scope){
4743         var r = [];
4744         for(var i = 0, len = this.config.length; i < len; i++){
4745             var c = this.config[i];
4746             if(fn.call(scope||this, c, i) === true){
4747                 r[r.length] = c;
4748             }
4749         }
4750         return r;
4751     },
4752
4753     /**
4754      * Returns true if the specified column is sortable.
4755      * @param {Number} col The column index
4756      * @return {Boolean}
4757      */
4758     isSortable : function(col){
4759         if(typeof this.config[col].sortable == "undefined"){
4760             return this.defaultSortable;
4761         }
4762         return this.config[col].sortable;
4763     },
4764
4765     /**
4766      * Returns the rendering (formatting) function defined for the column.
4767      * @param {Number} col The column index.
4768      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4769      */
4770     getRenderer : function(col){
4771         if(!this.config[col].renderer){
4772             return Roo.grid.ColumnModel.defaultRenderer;
4773         }
4774         return this.config[col].renderer;
4775     },
4776
4777     /**
4778      * Sets the rendering (formatting) function for a column.
4779      * @param {Number} col The column index
4780      * @param {Function} fn The function to use to process the cell's raw data
4781      * to return HTML markup for the grid view. The render function is called with
4782      * the following parameters:<ul>
4783      * <li>Data value.</li>
4784      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4785      * <li>css A CSS style string to apply to the table cell.</li>
4786      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4787      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4788      * <li>Row index</li>
4789      * <li>Column index</li>
4790      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4791      */
4792     setRenderer : function(col, fn){
4793         this.config[col].renderer = fn;
4794     },
4795
4796     /**
4797      * Returns the width for the specified column.
4798      * @param {Number} col The column index
4799      * @return {Number}
4800      */
4801     getColumnWidth : function(col){
4802         return this.config[col].width * 1 || this.defaultWidth;
4803     },
4804
4805     /**
4806      * Sets the width for a column.
4807      * @param {Number} col The column index
4808      * @param {Number} width The new width
4809      */
4810     setColumnWidth : function(col, width, suppressEvent){
4811         this.config[col].width = width;
4812         this.totalWidth = null;
4813         if(!suppressEvent){
4814              this.fireEvent("widthchange", this, col, width);
4815         }
4816     },
4817
4818     /**
4819      * Returns the total width of all columns.
4820      * @param {Boolean} includeHidden True to include hidden column widths
4821      * @return {Number}
4822      */
4823     getTotalWidth : function(includeHidden){
4824         if(!this.totalWidth){
4825             this.totalWidth = 0;
4826             for(var i = 0, len = this.config.length; i < len; i++){
4827                 if(includeHidden || !this.isHidden(i)){
4828                     this.totalWidth += this.getColumnWidth(i);
4829                 }
4830             }
4831         }
4832         return this.totalWidth;
4833     },
4834
4835     /**
4836      * Returns the header for the specified column.
4837      * @param {Number} col The column index
4838      * @return {String}
4839      */
4840     getColumnHeader : function(col){
4841         return this.config[col].header;
4842     },
4843
4844     /**
4845      * Sets the header for a column.
4846      * @param {Number} col The column index
4847      * @param {String} header The new header
4848      */
4849     setColumnHeader : function(col, header){
4850         this.config[col].header = header;
4851         this.fireEvent("headerchange", this, col, header);
4852     },
4853
4854     /**
4855      * Returns the tooltip for the specified column.
4856      * @param {Number} col The column index
4857      * @return {String}
4858      */
4859     getColumnTooltip : function(col){
4860             return this.config[col].tooltip;
4861     },
4862     /**
4863      * Sets the tooltip for a column.
4864      * @param {Number} col The column index
4865      * @param {String} tooltip The new tooltip
4866      */
4867     setColumnTooltip : function(col, tooltip){
4868             this.config[col].tooltip = tooltip;
4869     },
4870
4871     /**
4872      * Returns the dataIndex for the specified column.
4873      * @param {Number} col The column index
4874      * @return {Number}
4875      */
4876     getDataIndex : function(col){
4877         return this.config[col].dataIndex;
4878     },
4879
4880     /**
4881      * Sets the dataIndex for a column.
4882      * @param {Number} col The column index
4883      * @param {Number} dataIndex The new dataIndex
4884      */
4885     setDataIndex : function(col, dataIndex){
4886         this.config[col].dataIndex = dataIndex;
4887     },
4888
4889     
4890     
4891     /**
4892      * Returns true if the cell is editable.
4893      * @param {Number} colIndex The column index
4894      * @param {Number} rowIndex The row index
4895      * @return {Boolean}
4896      */
4897     isCellEditable : function(colIndex, rowIndex){
4898         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4899     },
4900
4901     /**
4902      * Returns the editor defined for the cell/column.
4903      * return false or null to disable editing.
4904      * @param {Number} colIndex The column index
4905      * @param {Number} rowIndex The row index
4906      * @return {Object}
4907      */
4908     getCellEditor : function(colIndex, rowIndex){
4909         return this.config[colIndex].editor;
4910     },
4911
4912     /**
4913      * Sets if a column is editable.
4914      * @param {Number} col The column index
4915      * @param {Boolean} editable True if the column is editable
4916      */
4917     setEditable : function(col, editable){
4918         this.config[col].editable = editable;
4919     },
4920
4921
4922     /**
4923      * Returns true if the column is hidden.
4924      * @param {Number} colIndex The column index
4925      * @return {Boolean}
4926      */
4927     isHidden : function(colIndex){
4928         return this.config[colIndex].hidden;
4929     },
4930
4931
4932     /**
4933      * Returns true if the column width cannot be changed
4934      */
4935     isFixed : function(colIndex){
4936         return this.config[colIndex].fixed;
4937     },
4938
4939     /**
4940      * Returns true if the column can be resized
4941      * @return {Boolean}
4942      */
4943     isResizable : function(colIndex){
4944         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4945     },
4946     /**
4947      * Sets if a column is hidden.
4948      * @param {Number} colIndex The column index
4949      * @param {Boolean} hidden True if the column is hidden
4950      */
4951     setHidden : function(colIndex, hidden){
4952         this.config[colIndex].hidden = hidden;
4953         this.totalWidth = null;
4954         this.fireEvent("hiddenchange", this, colIndex, hidden);
4955     },
4956
4957     /**
4958      * Sets the editor for a column.
4959      * @param {Number} col The column index
4960      * @param {Object} editor The editor object
4961      */
4962     setEditor : function(col, editor){
4963         this.config[col].editor = editor;
4964     }
4965 });
4966
4967 Roo.grid.ColumnModel.defaultRenderer = function(value){
4968         if(typeof value == "string" && value.length < 1){
4969             return "&#160;";
4970         }
4971         return value;
4972 };
4973
4974 // Alias for backwards compatibility
4975 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4976 /*
4977  * Based on:
4978  * Ext JS Library 1.1.1
4979  * Copyright(c) 2006-2007, Ext JS, LLC.
4980  *
4981  * Originally Released Under LGPL - original licence link has changed is not relivant.
4982  *
4983  * Fork - LGPL
4984  * <script type="text/javascript">
4985  */
4986  
4987 /**
4988  * @class Roo.LoadMask
4989  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4990  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4991  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4992  * element's UpdateManager load indicator and will be destroyed after the initial load.
4993  * @constructor
4994  * Create a new LoadMask
4995  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4996  * @param {Object} config The config object
4997  */
4998 Roo.LoadMask = function(el, config){
4999     this.el = Roo.get(el);
5000     Roo.apply(this, config);
5001     if(this.store){
5002         this.store.on('beforeload', this.onBeforeLoad, this);
5003         this.store.on('load', this.onLoad, this);
5004         this.store.on('loadexception', this.onLoadException, this);
5005         this.removeMask = false;
5006     }else{
5007         var um = this.el.getUpdateManager();
5008         um.showLoadIndicator = false; // disable the default indicator
5009         um.on('beforeupdate', this.onBeforeLoad, this);
5010         um.on('update', this.onLoad, this);
5011         um.on('failure', this.onLoad, this);
5012         this.removeMask = true;
5013     }
5014 };
5015
5016 Roo.LoadMask.prototype = {
5017     /**
5018      * @cfg {Boolean} removeMask
5019      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5020      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5021      */
5022     /**
5023      * @cfg {String} msg
5024      * The text to display in a centered loading message box (defaults to 'Loading...')
5025      */
5026     msg : 'Loading...',
5027     /**
5028      * @cfg {String} msgCls
5029      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5030      */
5031     msgCls : 'x-mask-loading',
5032
5033     /**
5034      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5035      * @type Boolean
5036      */
5037     disabled: false,
5038
5039     /**
5040      * Disables the mask to prevent it from being displayed
5041      */
5042     disable : function(){
5043        this.disabled = true;
5044     },
5045
5046     /**
5047      * Enables the mask so that it can be displayed
5048      */
5049     enable : function(){
5050         this.disabled = false;
5051     },
5052     
5053     onLoadException : function()
5054     {
5055         Roo.log(arguments);
5056         
5057         if (typeof(arguments[3]) != 'undefined') {
5058             Roo.MessageBox.alert("Error loading",arguments[3]);
5059         } 
5060         /*
5061         try {
5062             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5063                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5064             }   
5065         } catch(e) {
5066             
5067         }
5068         */
5069     
5070         
5071         
5072         this.el.unmask(this.removeMask);
5073     },
5074     // private
5075     onLoad : function()
5076     {
5077         this.el.unmask(this.removeMask);
5078     },
5079
5080     // private
5081     onBeforeLoad : function(){
5082         if(!this.disabled){
5083             this.el.mask(this.msg, this.msgCls);
5084         }
5085     },
5086
5087     // private
5088     destroy : function(){
5089         if(this.store){
5090             this.store.un('beforeload', this.onBeforeLoad, this);
5091             this.store.un('load', this.onLoad, this);
5092             this.store.un('loadexception', this.onLoadException, this);
5093         }else{
5094             var um = this.el.getUpdateManager();
5095             um.un('beforeupdate', this.onBeforeLoad, this);
5096             um.un('update', this.onLoad, this);
5097             um.un('failure', this.onLoad, this);
5098         }
5099     }
5100 };/*
5101  * - LGPL
5102  *
5103  * table
5104  * 
5105  */
5106
5107 /**
5108  * @class Roo.bootstrap.Table
5109  * @extends Roo.bootstrap.Component
5110  * Bootstrap Table class
5111  * @cfg {String} cls table class
5112  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5113  * @cfg {String} bgcolor Specifies the background color for a table
5114  * @cfg {Number} border Specifies whether the table cells should have borders or not
5115  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5116  * @cfg {Number} cellspacing Specifies the space between cells
5117  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5118  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5119  * @cfg {String} sortable Specifies that the table should be sortable
5120  * @cfg {String} summary Specifies a summary of the content of a table
5121  * @cfg {Number} width Specifies the width of a table
5122  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5123  * 
5124  * @cfg {boolean} striped Should the rows be alternative striped
5125  * @cfg {boolean} bordered Add borders to the table
5126  * @cfg {boolean} hover Add hover highlighting
5127  * @cfg {boolean} condensed Format condensed
5128  * @cfg {boolean} responsive Format condensed
5129  * @cfg {Boolean} loadMask (true|false) default false
5130  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5131  * @cfg {Boolean} thead (true|false) generate thead, default true
5132  * @cfg {Boolean} RowSelection (true|false) default false
5133  * @cfg {Boolean} CellSelection (true|false) default false
5134  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5135  
5136  * 
5137  * @constructor
5138  * Create a new Table
5139  * @param {Object} config The config object
5140  */
5141
5142 Roo.bootstrap.Table = function(config){
5143     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5144     
5145     if (this.sm) {
5146         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5147         this.sm = this.selModel;
5148         this.sm.xmodule = this.xmodule || false;
5149     }
5150     if (this.cm && typeof(this.cm.config) == 'undefined') {
5151         this.colModel = new Roo.grid.ColumnModel(this.cm);
5152         this.cm = this.colModel;
5153         this.cm.xmodule = this.xmodule || false;
5154     }
5155     if (this.store) {
5156         this.store= Roo.factory(this.store, Roo.data);
5157         this.ds = this.store;
5158         this.ds.xmodule = this.xmodule || false;
5159          
5160     }
5161     if (this.footer && this.store) {
5162         this.footer.dataSource = this.ds;
5163         this.footer = Roo.factory(this.footer);
5164     }
5165     
5166     /** @private */
5167     this.addEvents({
5168         /**
5169          * @event cellclick
5170          * Fires when a cell is clicked
5171          * @param {Roo.bootstrap.Table} this
5172          * @param {Roo.Element} el
5173          * @param {Number} rowIndex
5174          * @param {Number} columnIndex
5175          * @param {Roo.EventObject} e
5176          */
5177         "cellclick" : true,
5178         /**
5179          * @event celldblclick
5180          * Fires when a cell is double clicked
5181          * @param {Roo.bootstrap.Table} this
5182          * @param {Roo.Element} el
5183          * @param {Number} rowIndex
5184          * @param {Number} columnIndex
5185          * @param {Roo.EventObject} e
5186          */
5187         "celldblclick" : true,
5188         /**
5189          * @event rowclick
5190          * Fires when a row is clicked
5191          * @param {Roo.bootstrap.Table} this
5192          * @param {Roo.Element} el
5193          * @param {Number} rowIndex
5194          * @param {Roo.EventObject} e
5195          */
5196         "rowclick" : true,
5197         /**
5198          * @event rowdblclick
5199          * Fires when a row is double clicked
5200          * @param {Roo.bootstrap.Table} this
5201          * @param {Roo.Element} el
5202          * @param {Number} rowIndex
5203          * @param {Roo.EventObject} e
5204          */
5205         "rowdblclick" : true,
5206         /**
5207          * @event mouseover
5208          * Fires when a mouseover occur
5209          * @param {Roo.bootstrap.Table} this
5210          * @param {Roo.Element} el
5211          * @param {Number} rowIndex
5212          * @param {Number} columnIndex
5213          * @param {Roo.EventObject} e
5214          */
5215         "mouseover" : true,
5216         /**
5217          * @event mouseout
5218          * Fires when a mouseout occur
5219          * @param {Roo.bootstrap.Table} this
5220          * @param {Roo.Element} el
5221          * @param {Number} rowIndex
5222          * @param {Number} columnIndex
5223          * @param {Roo.EventObject} e
5224          */
5225         "mouseout" : true,
5226         /**
5227          * @event rowclass
5228          * Fires when a row is rendered, so you can change add a style to it.
5229          * @param {Roo.bootstrap.Table} this
5230          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5231          */
5232         'rowclass' : true,
5233           /**
5234          * @event rowsrendered
5235          * Fires when all the  rows have been rendered
5236          * @param {Roo.bootstrap.Table} this
5237          */
5238         'rowsrendered' : true
5239         
5240     });
5241 };
5242
5243 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5244     
5245     cls: false,
5246     align: false,
5247     bgcolor: false,
5248     border: false,
5249     cellpadding: false,
5250     cellspacing: false,
5251     frame: false,
5252     rules: false,
5253     sortable: false,
5254     summary: false,
5255     width: false,
5256     striped : false,
5257     bordered: false,
5258     hover:  false,
5259     condensed : false,
5260     responsive : false,
5261     sm : false,
5262     cm : false,
5263     store : false,
5264     loadMask : false,
5265     tfoot : true,
5266     thead : true,
5267     RowSelection : false,
5268     CellSelection : false,
5269     layout : false,
5270     
5271     // Roo.Element - the tbody
5272     mainBody: false, 
5273     
5274     getAutoCreate : function(){
5275         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5276         
5277         cfg = {
5278             tag: 'table',
5279             cls : 'table',
5280             cn : []
5281         }
5282             
5283         if (this.striped) {
5284             cfg.cls += ' table-striped';
5285         }
5286         
5287         if (this.hover) {
5288             cfg.cls += ' table-hover';
5289         }
5290         if (this.bordered) {
5291             cfg.cls += ' table-bordered';
5292         }
5293         if (this.condensed) {
5294             cfg.cls += ' table-condensed';
5295         }
5296         if (this.responsive) {
5297             cfg.cls += ' table-responsive';
5298         }
5299         
5300         if (this.cls) {
5301             cfg.cls+=  ' ' +this.cls;
5302         }
5303         
5304         // this lot should be simplifed...
5305         
5306         if (this.align) {
5307             cfg.align=this.align;
5308         }
5309         if (this.bgcolor) {
5310             cfg.bgcolor=this.bgcolor;
5311         }
5312         if (this.border) {
5313             cfg.border=this.border;
5314         }
5315         if (this.cellpadding) {
5316             cfg.cellpadding=this.cellpadding;
5317         }
5318         if (this.cellspacing) {
5319             cfg.cellspacing=this.cellspacing;
5320         }
5321         if (this.frame) {
5322             cfg.frame=this.frame;
5323         }
5324         if (this.rules) {
5325             cfg.rules=this.rules;
5326         }
5327         if (this.sortable) {
5328             cfg.sortable=this.sortable;
5329         }
5330         if (this.summary) {
5331             cfg.summary=this.summary;
5332         }
5333         if (this.width) {
5334             cfg.width=this.width;
5335         }
5336         if (this.layout) {
5337             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5338         }
5339         
5340         if(this.store || this.cm){
5341             if(this.thead){
5342                 cfg.cn.push(this.renderHeader());
5343             }
5344             
5345             cfg.cn.push(this.renderBody());
5346             
5347             if(this.tfoot){
5348                 cfg.cn.push(this.renderFooter());
5349             }
5350             
5351             cfg.cls+=  ' TableGrid';
5352         }
5353         
5354         return { cn : [ cfg ] };
5355     },
5356     
5357     initEvents : function()
5358     {   
5359         if(!this.store || !this.cm){
5360             return;
5361         }
5362         
5363         //Roo.log('initEvents with ds!!!!');
5364         
5365         this.mainBody = this.el.select('tbody', true).first();
5366         
5367         
5368         var _this = this;
5369         
5370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5371             e.on('click', _this.sort, _this);
5372         });
5373         
5374         this.el.on("click", this.onClick, this);
5375         this.el.on("dblclick", this.onDblClick, this);
5376         
5377         // why is this done????? = it breaks dialogs??
5378         //this.parent().el.setStyle('position', 'relative');
5379         
5380         
5381         if (this.footer) {
5382             this.footer.parentId = this.id;
5383             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5384         }
5385         
5386         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5387         
5388         this.store.on('load', this.onLoad, this);
5389         this.store.on('beforeload', this.onBeforeLoad, this);
5390         this.store.on('update', this.onUpdate, this);
5391         this.store.on('add', this.onAdd, this);
5392         
5393     },
5394     
5395     onMouseover : function(e, el)
5396     {
5397         var cell = Roo.get(el);
5398         
5399         if(!cell){
5400             return;
5401         }
5402         
5403         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5404             cell = cell.findParent('td', false, true);
5405         }
5406         
5407         var row = cell.findParent('tr', false, true);
5408         var cellIndex = cell.dom.cellIndex;
5409         var rowIndex = row.dom.rowIndex - 1; // start from 0
5410         
5411         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5412         
5413     },
5414     
5415     onMouseout : function(e, el)
5416     {
5417         var cell = Roo.get(el);
5418         
5419         if(!cell){
5420             return;
5421         }
5422         
5423         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5424             cell = cell.findParent('td', false, true);
5425         }
5426         
5427         var row = cell.findParent('tr', false, true);
5428         var cellIndex = cell.dom.cellIndex;
5429         var rowIndex = row.dom.rowIndex - 1; // start from 0
5430         
5431         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5432         
5433     },
5434     
5435     onClick : function(e, el)
5436     {
5437         var cell = Roo.get(el);
5438         
5439         if(!cell || (!this.CellSelection && !this.RowSelection)){
5440             return;
5441         }
5442         
5443         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5444             cell = cell.findParent('td', false, true);
5445         }
5446         
5447         if(!cell || typeof(cell) == 'undefined'){
5448             return;
5449         }
5450         
5451         var row = cell.findParent('tr', false, true);
5452         
5453         if(!row || typeof(row) == 'undefined'){
5454             return;
5455         }
5456         
5457         var cellIndex = cell.dom.cellIndex;
5458         var rowIndex = this.getRowIndex(row);
5459         
5460         if(this.CellSelection){
5461             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5462         }
5463         
5464         if(this.RowSelection){
5465             this.fireEvent('rowclick', this, row, rowIndex, e);
5466         }
5467         
5468         
5469     },
5470     
5471     onDblClick : function(e,el)
5472     {
5473         var cell = Roo.get(el);
5474         
5475         if(!cell || (!this.CellSelection && !this.RowSelection)){
5476             return;
5477         }
5478         
5479         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5480             cell = cell.findParent('td', false, true);
5481         }
5482         
5483         if(!cell || typeof(cell) == 'undefined'){
5484             return;
5485         }
5486         
5487         var row = cell.findParent('tr', false, true);
5488         
5489         if(!row || typeof(row) == 'undefined'){
5490             return;
5491         }
5492         
5493         var cellIndex = cell.dom.cellIndex;
5494         var rowIndex = this.getRowIndex(row);
5495         
5496         if(this.CellSelection){
5497             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5498         }
5499         
5500         if(this.RowSelection){
5501             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5502         }
5503     },
5504     
5505     sort : function(e,el)
5506     {
5507         var col = Roo.get(el);
5508         
5509         if(!col.hasClass('sortable')){
5510             return;
5511         }
5512         
5513         var sort = col.attr('sort');
5514         var dir = 'ASC';
5515         
5516         if(col.hasClass('glyphicon-arrow-up')){
5517             dir = 'DESC';
5518         }
5519         
5520         this.store.sortInfo = {field : sort, direction : dir};
5521         
5522         if (this.footer) {
5523             Roo.log("calling footer first");
5524             this.footer.onClick('first');
5525         } else {
5526         
5527             this.store.load({ params : { start : 0 } });
5528         }
5529     },
5530     
5531     renderHeader : function()
5532     {
5533         var header = {
5534             tag: 'thead',
5535             cn : []
5536         };
5537         
5538         var cm = this.cm;
5539         
5540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5541             
5542             var config = cm.config[i];
5543                     
5544             var c = {
5545                 tag: 'th',
5546                 style : '',
5547                 html: cm.getColumnHeader(i)
5548             };
5549             
5550             if(typeof(config.tooltip) != 'undefined'){
5551                 c.tooltip = config.tooltip;
5552             }
5553             
5554             if(typeof(config.colspan) != 'undefined'){
5555                 c.colspan = config.colspan;
5556             }
5557             
5558             if(typeof(config.hidden) != 'undefined' && config.hidden){
5559                 c.style += ' display:none;';
5560             }
5561             
5562             if(typeof(config.dataIndex) != 'undefined'){
5563                 c.sort = config.dataIndex;
5564             }
5565             
5566             if(typeof(config.sortable) != 'undefined' && config.sortable){
5567                 c.cls = 'sortable';
5568             }
5569             
5570             if(typeof(config.align) != 'undefined' && config.align.length){
5571                 c.style += ' text-align:' + config.align + ';';
5572             }
5573             
5574             if(typeof(config.width) != 'undefined'){
5575                 c.style += ' width:' + config.width + 'px;';
5576             }
5577             
5578             if(typeof(config.cls) != 'undefined'){
5579                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5580             }
5581             
5582             header.cn.push(c)
5583         }
5584         
5585         return header;
5586     },
5587     
5588     renderBody : function()
5589     {
5590         var body = {
5591             tag: 'tbody',
5592             cn : [
5593                 {
5594                     tag: 'tr',
5595                     cn : [
5596                         {
5597                             tag : 'td',
5598                             colspan :  this.cm.getColumnCount()
5599                         }
5600                     ]
5601                 }
5602             ]
5603         };
5604         
5605         return body;
5606     },
5607     
5608     renderFooter : function()
5609     {
5610         var footer = {
5611             tag: 'tfoot',
5612             cn : [
5613                 {
5614                     tag: 'tr',
5615                     cn : [
5616                         {
5617                             tag : 'td',
5618                             colspan :  this.cm.getColumnCount()
5619                         }
5620                     ]
5621                 }
5622             ]
5623         };
5624         
5625         return footer;
5626     },
5627     
5628     
5629     
5630     onLoad : function()
5631     {
5632         Roo.log('ds onload');
5633         this.clear();
5634         
5635         var _this = this;
5636         var cm = this.cm;
5637         var ds = this.store;
5638         
5639         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5640             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5641             
5642             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5643                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5644             }
5645             
5646             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5647                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5648             }
5649         });
5650         
5651         var tbody =  this.mainBody;
5652               
5653         if(ds.getCount() > 0){
5654             ds.data.each(function(d,rowIndex){
5655                 var row =  this.renderRow(cm, ds, rowIndex);
5656                 
5657                 tbody.createChild(row);
5658                 
5659                 var _this = this;
5660                 
5661                 if(row.cellObjects.length){
5662                     Roo.each(row.cellObjects, function(r){
5663                         _this.renderCellObject(r);
5664                     })
5665                 }
5666                 
5667             }, this);
5668         }
5669         
5670         Roo.each(this.el.select('tbody td', true).elements, function(e){
5671             e.on('mouseover', _this.onMouseover, _this);
5672         });
5673         
5674         Roo.each(this.el.select('tbody td', true).elements, function(e){
5675             e.on('mouseout', _this.onMouseout, _this);
5676         });
5677         this.fireEvent('rowsrendered', this);
5678         //if(this.loadMask){
5679         //    this.maskEl.hide();
5680         //}
5681     },
5682     
5683     
5684     onUpdate : function(ds,record)
5685     {
5686         this.refreshRow(record);
5687     },
5688     
5689     onRemove : function(ds, record, index, isUpdate){
5690         if(isUpdate !== true){
5691             this.fireEvent("beforerowremoved", this, index, record);
5692         }
5693         var bt = this.mainBody.dom;
5694         
5695         var rows = this.el.select('tbody > tr', true).elements;
5696         
5697         if(typeof(rows[index]) != 'undefined'){
5698             bt.removeChild(rows[index].dom);
5699         }
5700         
5701 //        if(bt.rows[index]){
5702 //            bt.removeChild(bt.rows[index]);
5703 //        }
5704         
5705         if(isUpdate !== true){
5706             //this.stripeRows(index);
5707             //this.syncRowHeights(index, index);
5708             //this.layout();
5709             this.fireEvent("rowremoved", this, index, record);
5710         }
5711     },
5712     
5713     onAdd : function(ds, records, rowIndex)
5714     {
5715         //Roo.log('on Add called');
5716         // - note this does not handle multiple adding very well..
5717         var bt = this.mainBody.dom;
5718         for (var i =0 ; i < records.length;i++) {
5719             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5720             //Roo.log(records[i]);
5721             //Roo.log(this.store.getAt(rowIndex+i));
5722             this.insertRow(this.store, rowIndex + i, false);
5723             return;
5724         }
5725         
5726     },
5727     
5728     
5729     refreshRow : function(record){
5730         var ds = this.store, index;
5731         if(typeof record == 'number'){
5732             index = record;
5733             record = ds.getAt(index);
5734         }else{
5735             index = ds.indexOf(record);
5736         }
5737         this.insertRow(ds, index, true);
5738         this.onRemove(ds, record, index+1, true);
5739         //this.syncRowHeights(index, index);
5740         //this.layout();
5741         this.fireEvent("rowupdated", this, index, record);
5742     },
5743     
5744     insertRow : function(dm, rowIndex, isUpdate){
5745         
5746         if(!isUpdate){
5747             this.fireEvent("beforerowsinserted", this, rowIndex);
5748         }
5749             //var s = this.getScrollState();
5750         var row = this.renderRow(this.cm, this.store, rowIndex);
5751         // insert before rowIndex..
5752         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5753         
5754         var _this = this;
5755                 
5756         if(row.cellObjects.length){
5757             Roo.each(row.cellObjects, function(r){
5758                 _this.renderCellObject(r);
5759             })
5760         }
5761             
5762         if(!isUpdate){
5763             this.fireEvent("rowsinserted", this, rowIndex);
5764             //this.syncRowHeights(firstRow, lastRow);
5765             //this.stripeRows(firstRow);
5766             //this.layout();
5767         }
5768         
5769     },
5770     
5771     
5772     getRowDom : function(rowIndex)
5773     {
5774         var rows = this.el.select('tbody > tr', true).elements;
5775         
5776         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5777         
5778     },
5779     // returns the object tree for a tr..
5780   
5781     
5782     renderRow : function(cm, ds, rowIndex) 
5783     {
5784         
5785         var d = ds.getAt(rowIndex);
5786         
5787         var row = {
5788             tag : 'tr',
5789             cn : []
5790         };
5791             
5792         var cellObjects = [];
5793         
5794         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5795             var config = cm.config[i];
5796             
5797             var renderer = cm.getRenderer(i);
5798             var value = '';
5799             var id = false;
5800             
5801             if(typeof(renderer) !== 'undefined'){
5802                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5803             }
5804             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5805             // and are rendered into the cells after the row is rendered - using the id for the element.
5806             
5807             if(typeof(value) === 'object'){
5808                 id = Roo.id();
5809                 cellObjects.push({
5810                     container : id,
5811                     cfg : value 
5812                 })
5813             }
5814             
5815             var rowcfg = {
5816                 record: d,
5817                 rowIndex : rowIndex,
5818                 colIndex : i,
5819                 rowClass : ''
5820             }
5821
5822             this.fireEvent('rowclass', this, rowcfg);
5823             
5824             var td = {
5825                 tag: 'td',
5826                 cls : rowcfg.rowClass,
5827                 style: '',
5828                 html: (typeof(value) === 'object') ? '' : value
5829             };
5830             
5831             if (id) {
5832                 td.id = id;
5833             }
5834             
5835             if(typeof(config.colspan) != 'undefined'){
5836                 td.colspan = config.colspan;
5837             }
5838             
5839             if(typeof(config.hidden) != 'undefined' && config.hidden){
5840                 td.style += ' display:none;';
5841             }
5842             
5843             if(typeof(config.align) != 'undefined' && config.align.length){
5844                 td.style += ' text-align:' + config.align + ';';
5845             }
5846             
5847             if(typeof(config.width) != 'undefined'){
5848                 td.style += ' width:' +  config.width + 'px;';
5849             }
5850             
5851             if(typeof(config.cursor) != 'undefined'){
5852                 td.style += ' cursor:' +  config.cursor + ';';
5853             }
5854             
5855             if(typeof(config.cls) != 'undefined'){
5856                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5857             }
5858              
5859             row.cn.push(td);
5860            
5861         }
5862         
5863         row.cellObjects = cellObjects;
5864         
5865         return row;
5866           
5867     },
5868     
5869     
5870     
5871     onBeforeLoad : function()
5872     {
5873         //Roo.log('ds onBeforeLoad');
5874         
5875         //this.clear();
5876         
5877         //if(this.loadMask){
5878         //    this.maskEl.show();
5879         //}
5880     },
5881      /**
5882      * Remove all rows
5883      */
5884     clear : function()
5885     {
5886         this.el.select('tbody', true).first().dom.innerHTML = '';
5887     },
5888     /**
5889      * Show or hide a row.
5890      * @param {Number} rowIndex to show or hide
5891      * @param {Boolean} state hide
5892      */
5893     setRowVisibility : function(rowIndex, state)
5894     {
5895         var bt = this.mainBody.dom;
5896         
5897         var rows = this.el.select('tbody > tr', true).elements;
5898         
5899         if(typeof(rows[rowIndex]) == 'undefined'){
5900             return;
5901         }
5902         rows[rowIndex].dom.style.display = state ? '' : 'none';
5903     },
5904     
5905     
5906     getSelectionModel : function(){
5907         if(!this.selModel){
5908             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5909         }
5910         return this.selModel;
5911     },
5912     /*
5913      * Render the Roo.bootstrap object from renderder
5914      */
5915     renderCellObject : function(r)
5916     {
5917         var _this = this;
5918         
5919         var t = r.cfg.render(r.container);
5920         
5921         if(r.cfg.cn){
5922             Roo.each(r.cfg.cn, function(c){
5923                 var child = {
5924                     container: t.getChildContainer(),
5925                     cfg: c
5926                 }
5927                 _this.renderCellObject(child);
5928             })
5929         }
5930     },
5931     
5932     getRowIndex : function(row)
5933     {
5934         var rowIndex = -1;
5935         
5936         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5937             if(el != row){
5938                 return;
5939             }
5940             
5941             rowIndex = index;
5942         });
5943         
5944         return rowIndex;
5945     }
5946    
5947 });
5948
5949  
5950
5951  /*
5952  * - LGPL
5953  *
5954  * table cell
5955  * 
5956  */
5957
5958 /**
5959  * @class Roo.bootstrap.TableCell
5960  * @extends Roo.bootstrap.Component
5961  * Bootstrap TableCell class
5962  * @cfg {String} html cell contain text
5963  * @cfg {String} cls cell class
5964  * @cfg {String} tag cell tag (td|th) default td
5965  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5966  * @cfg {String} align Aligns the content in a cell
5967  * @cfg {String} axis Categorizes cells
5968  * @cfg {String} bgcolor Specifies the background color of a cell
5969  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5970  * @cfg {Number} colspan Specifies the number of columns a cell should span
5971  * @cfg {String} headers Specifies one or more header cells a cell is related to
5972  * @cfg {Number} height Sets the height of a cell
5973  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5974  * @cfg {Number} rowspan Sets the number of rows a cell should span
5975  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5976  * @cfg {String} valign Vertical aligns the content in a cell
5977  * @cfg {Number} width Specifies the width of a cell
5978  * 
5979  * @constructor
5980  * Create a new TableCell
5981  * @param {Object} config The config object
5982  */
5983
5984 Roo.bootstrap.TableCell = function(config){
5985     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5986 };
5987
5988 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5989     
5990     html: false,
5991     cls: false,
5992     tag: false,
5993     abbr: false,
5994     align: false,
5995     axis: false,
5996     bgcolor: false,
5997     charoff: false,
5998     colspan: false,
5999     headers: false,
6000     height: false,
6001     nowrap: false,
6002     rowspan: false,
6003     scope: false,
6004     valign: false,
6005     width: false,
6006     
6007     
6008     getAutoCreate : function(){
6009         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6010         
6011         cfg = {
6012             tag: 'td'
6013         }
6014         
6015         if(this.tag){
6016             cfg.tag = this.tag;
6017         }
6018         
6019         if (this.html) {
6020             cfg.html=this.html
6021         }
6022         if (this.cls) {
6023             cfg.cls=this.cls
6024         }
6025         if (this.abbr) {
6026             cfg.abbr=this.abbr
6027         }
6028         if (this.align) {
6029             cfg.align=this.align
6030         }
6031         if (this.axis) {
6032             cfg.axis=this.axis
6033         }
6034         if (this.bgcolor) {
6035             cfg.bgcolor=this.bgcolor
6036         }
6037         if (this.charoff) {
6038             cfg.charoff=this.charoff
6039         }
6040         if (this.colspan) {
6041             cfg.colspan=this.colspan
6042         }
6043         if (this.headers) {
6044             cfg.headers=this.headers
6045         }
6046         if (this.height) {
6047             cfg.height=this.height
6048         }
6049         if (this.nowrap) {
6050             cfg.nowrap=this.nowrap
6051         }
6052         if (this.rowspan) {
6053             cfg.rowspan=this.rowspan
6054         }
6055         if (this.scope) {
6056             cfg.scope=this.scope
6057         }
6058         if (this.valign) {
6059             cfg.valign=this.valign
6060         }
6061         if (this.width) {
6062             cfg.width=this.width
6063         }
6064         
6065         
6066         return cfg;
6067     }
6068    
6069 });
6070
6071  
6072
6073  /*
6074  * - LGPL
6075  *
6076  * table row
6077  * 
6078  */
6079
6080 /**
6081  * @class Roo.bootstrap.TableRow
6082  * @extends Roo.bootstrap.Component
6083  * Bootstrap TableRow class
6084  * @cfg {String} cls row class
6085  * @cfg {String} align Aligns the content in a table row
6086  * @cfg {String} bgcolor Specifies a background color for a table row
6087  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6088  * @cfg {String} valign Vertical aligns the content in a table row
6089  * 
6090  * @constructor
6091  * Create a new TableRow
6092  * @param {Object} config The config object
6093  */
6094
6095 Roo.bootstrap.TableRow = function(config){
6096     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6097 };
6098
6099 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6100     
6101     cls: false,
6102     align: false,
6103     bgcolor: false,
6104     charoff: false,
6105     valign: false,
6106     
6107     getAutoCreate : function(){
6108         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6109         
6110         cfg = {
6111             tag: 'tr'
6112         }
6113             
6114         if(this.cls){
6115             cfg.cls = this.cls;
6116         }
6117         if(this.align){
6118             cfg.align = this.align;
6119         }
6120         if(this.bgcolor){
6121             cfg.bgcolor = this.bgcolor;
6122         }
6123         if(this.charoff){
6124             cfg.charoff = this.charoff;
6125         }
6126         if(this.valign){
6127             cfg.valign = this.valign;
6128         }
6129         
6130         return cfg;
6131     }
6132    
6133 });
6134
6135  
6136
6137  /*
6138  * - LGPL
6139  *
6140  * table body
6141  * 
6142  */
6143
6144 /**
6145  * @class Roo.bootstrap.TableBody
6146  * @extends Roo.bootstrap.Component
6147  * Bootstrap TableBody class
6148  * @cfg {String} cls element class
6149  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6150  * @cfg {String} align Aligns the content inside the element
6151  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6152  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6153  * 
6154  * @constructor
6155  * Create a new TableBody
6156  * @param {Object} config The config object
6157  */
6158
6159 Roo.bootstrap.TableBody = function(config){
6160     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6161 };
6162
6163 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6164     
6165     cls: false,
6166     tag: false,
6167     align: false,
6168     charoff: false,
6169     valign: false,
6170     
6171     getAutoCreate : function(){
6172         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6173         
6174         cfg = {
6175             tag: 'tbody'
6176         }
6177             
6178         if (this.cls) {
6179             cfg.cls=this.cls
6180         }
6181         if(this.tag){
6182             cfg.tag = this.tag;
6183         }
6184         
6185         if(this.align){
6186             cfg.align = this.align;
6187         }
6188         if(this.charoff){
6189             cfg.charoff = this.charoff;
6190         }
6191         if(this.valign){
6192             cfg.valign = this.valign;
6193         }
6194         
6195         return cfg;
6196     }
6197     
6198     
6199 //    initEvents : function()
6200 //    {
6201 //        
6202 //        if(!this.store){
6203 //            return;
6204 //        }
6205 //        
6206 //        this.store = Roo.factory(this.store, Roo.data);
6207 //        this.store.on('load', this.onLoad, this);
6208 //        
6209 //        this.store.load();
6210 //        
6211 //    },
6212 //    
6213 //    onLoad: function () 
6214 //    {   
6215 //        this.fireEvent('load', this);
6216 //    }
6217 //    
6218 //   
6219 });
6220
6221  
6222
6223  /*
6224  * Based on:
6225  * Ext JS Library 1.1.1
6226  * Copyright(c) 2006-2007, Ext JS, LLC.
6227  *
6228  * Originally Released Under LGPL - original licence link has changed is not relivant.
6229  *
6230  * Fork - LGPL
6231  * <script type="text/javascript">
6232  */
6233
6234 // as we use this in bootstrap.
6235 Roo.namespace('Roo.form');
6236  /**
6237  * @class Roo.form.Action
6238  * Internal Class used to handle form actions
6239  * @constructor
6240  * @param {Roo.form.BasicForm} el The form element or its id
6241  * @param {Object} config Configuration options
6242  */
6243
6244  
6245  
6246 // define the action interface
6247 Roo.form.Action = function(form, options){
6248     this.form = form;
6249     this.options = options || {};
6250 };
6251 /**
6252  * Client Validation Failed
6253  * @const 
6254  */
6255 Roo.form.Action.CLIENT_INVALID = 'client';
6256 /**
6257  * Server Validation Failed
6258  * @const 
6259  */
6260 Roo.form.Action.SERVER_INVALID = 'server';
6261  /**
6262  * Connect to Server Failed
6263  * @const 
6264  */
6265 Roo.form.Action.CONNECT_FAILURE = 'connect';
6266 /**
6267  * Reading Data from Server Failed
6268  * @const 
6269  */
6270 Roo.form.Action.LOAD_FAILURE = 'load';
6271
6272 Roo.form.Action.prototype = {
6273     type : 'default',
6274     failureType : undefined,
6275     response : undefined,
6276     result : undefined,
6277
6278     // interface method
6279     run : function(options){
6280
6281     },
6282
6283     // interface method
6284     success : function(response){
6285
6286     },
6287
6288     // interface method
6289     handleResponse : function(response){
6290
6291     },
6292
6293     // default connection failure
6294     failure : function(response){
6295         
6296         this.response = response;
6297         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6298         this.form.afterAction(this, false);
6299     },
6300
6301     processResponse : function(response){
6302         this.response = response;
6303         if(!response.responseText){
6304             return true;
6305         }
6306         this.result = this.handleResponse(response);
6307         return this.result;
6308     },
6309
6310     // utility functions used internally
6311     getUrl : function(appendParams){
6312         var url = this.options.url || this.form.url || this.form.el.dom.action;
6313         if(appendParams){
6314             var p = this.getParams();
6315             if(p){
6316                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6317             }
6318         }
6319         return url;
6320     },
6321
6322     getMethod : function(){
6323         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6324     },
6325
6326     getParams : function(){
6327         var bp = this.form.baseParams;
6328         var p = this.options.params;
6329         if(p){
6330             if(typeof p == "object"){
6331                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6332             }else if(typeof p == 'string' && bp){
6333                 p += '&' + Roo.urlEncode(bp);
6334             }
6335         }else if(bp){
6336             p = Roo.urlEncode(bp);
6337         }
6338         return p;
6339     },
6340
6341     createCallback : function(){
6342         return {
6343             success: this.success,
6344             failure: this.failure,
6345             scope: this,
6346             timeout: (this.form.timeout*1000),
6347             upload: this.form.fileUpload ? this.success : undefined
6348         };
6349     }
6350 };
6351
6352 Roo.form.Action.Submit = function(form, options){
6353     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6354 };
6355
6356 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6357     type : 'submit',
6358
6359     haveProgress : false,
6360     uploadComplete : false,
6361     
6362     // uploadProgress indicator.
6363     uploadProgress : function()
6364     {
6365         if (!this.form.progressUrl) {
6366             return;
6367         }
6368         
6369         if (!this.haveProgress) {
6370             Roo.MessageBox.progress("Uploading", "Uploading");
6371         }
6372         if (this.uploadComplete) {
6373            Roo.MessageBox.hide();
6374            return;
6375         }
6376         
6377         this.haveProgress = true;
6378    
6379         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6380         
6381         var c = new Roo.data.Connection();
6382         c.request({
6383             url : this.form.progressUrl,
6384             params: {
6385                 id : uid
6386             },
6387             method: 'GET',
6388             success : function(req){
6389                //console.log(data);
6390                 var rdata = false;
6391                 var edata;
6392                 try  {
6393                    rdata = Roo.decode(req.responseText)
6394                 } catch (e) {
6395                     Roo.log("Invalid data from server..");
6396                     Roo.log(edata);
6397                     return;
6398                 }
6399                 if (!rdata || !rdata.success) {
6400                     Roo.log(rdata);
6401                     Roo.MessageBox.alert(Roo.encode(rdata));
6402                     return;
6403                 }
6404                 var data = rdata.data;
6405                 
6406                 if (this.uploadComplete) {
6407                    Roo.MessageBox.hide();
6408                    return;
6409                 }
6410                    
6411                 if (data){
6412                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6413                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6414                     );
6415                 }
6416                 this.uploadProgress.defer(2000,this);
6417             },
6418        
6419             failure: function(data) {
6420                 Roo.log('progress url failed ');
6421                 Roo.log(data);
6422             },
6423             scope : this
6424         });
6425            
6426     },
6427     
6428     
6429     run : function()
6430     {
6431         // run get Values on the form, so it syncs any secondary forms.
6432         this.form.getValues();
6433         
6434         var o = this.options;
6435         var method = this.getMethod();
6436         var isPost = method == 'POST';
6437         if(o.clientValidation === false || this.form.isValid()){
6438             
6439             if (this.form.progressUrl) {
6440                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6441                     (new Date() * 1) + '' + Math.random());
6442                     
6443             } 
6444             
6445             
6446             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6447                 form:this.form.el.dom,
6448                 url:this.getUrl(!isPost),
6449                 method: method,
6450                 params:isPost ? this.getParams() : null,
6451                 isUpload: this.form.fileUpload
6452             }));
6453             
6454             this.uploadProgress();
6455
6456         }else if (o.clientValidation !== false){ // client validation failed
6457             this.failureType = Roo.form.Action.CLIENT_INVALID;
6458             this.form.afterAction(this, false);
6459         }
6460     },
6461
6462     success : function(response)
6463     {
6464         this.uploadComplete= true;
6465         if (this.haveProgress) {
6466             Roo.MessageBox.hide();
6467         }
6468         
6469         
6470         var result = this.processResponse(response);
6471         if(result === true || result.success){
6472             this.form.afterAction(this, true);
6473             return;
6474         }
6475         if(result.errors){
6476             this.form.markInvalid(result.errors);
6477             this.failureType = Roo.form.Action.SERVER_INVALID;
6478         }
6479         this.form.afterAction(this, false);
6480     },
6481     failure : function(response)
6482     {
6483         this.uploadComplete= true;
6484         if (this.haveProgress) {
6485             Roo.MessageBox.hide();
6486         }
6487         
6488         this.response = response;
6489         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6490         this.form.afterAction(this, false);
6491     },
6492     
6493     handleResponse : function(response){
6494         if(this.form.errorReader){
6495             var rs = this.form.errorReader.read(response);
6496             var errors = [];
6497             if(rs.records){
6498                 for(var i = 0, len = rs.records.length; i < len; i++) {
6499                     var r = rs.records[i];
6500                     errors[i] = r.data;
6501                 }
6502             }
6503             if(errors.length < 1){
6504                 errors = null;
6505             }
6506             return {
6507                 success : rs.success,
6508                 errors : errors
6509             };
6510         }
6511         var ret = false;
6512         try {
6513             ret = Roo.decode(response.responseText);
6514         } catch (e) {
6515             ret = {
6516                 success: false,
6517                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6518                 errors : []
6519             };
6520         }
6521         return ret;
6522         
6523     }
6524 });
6525
6526
6527 Roo.form.Action.Load = function(form, options){
6528     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6529     this.reader = this.form.reader;
6530 };
6531
6532 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6533     type : 'load',
6534
6535     run : function(){
6536         
6537         Roo.Ajax.request(Roo.apply(
6538                 this.createCallback(), {
6539                     method:this.getMethod(),
6540                     url:this.getUrl(false),
6541                     params:this.getParams()
6542         }));
6543     },
6544
6545     success : function(response){
6546         
6547         var result = this.processResponse(response);
6548         if(result === true || !result.success || !result.data){
6549             this.failureType = Roo.form.Action.LOAD_FAILURE;
6550             this.form.afterAction(this, false);
6551             return;
6552         }
6553         this.form.clearInvalid();
6554         this.form.setValues(result.data);
6555         this.form.afterAction(this, true);
6556     },
6557
6558     handleResponse : function(response){
6559         if(this.form.reader){
6560             var rs = this.form.reader.read(response);
6561             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6562             return {
6563                 success : rs.success,
6564                 data : data
6565             };
6566         }
6567         return Roo.decode(response.responseText);
6568     }
6569 });
6570
6571 Roo.form.Action.ACTION_TYPES = {
6572     'load' : Roo.form.Action.Load,
6573     'submit' : Roo.form.Action.Submit
6574 };/*
6575  * - LGPL
6576  *
6577  * form
6578  * 
6579  */
6580
6581 /**
6582  * @class Roo.bootstrap.Form
6583  * @extends Roo.bootstrap.Component
6584  * Bootstrap Form class
6585  * @cfg {String} method  GET | POST (default POST)
6586  * @cfg {String} labelAlign top | left (default top)
6587  * @cfg {String} align left  | right - for navbars
6588  * @cfg {Boolean} loadMask load mask when submit (default true)
6589
6590  * 
6591  * @constructor
6592  * Create a new Form
6593  * @param {Object} config The config object
6594  */
6595
6596
6597 Roo.bootstrap.Form = function(config){
6598     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6599     this.addEvents({
6600         /**
6601          * @event clientvalidation
6602          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6603          * @param {Form} this
6604          * @param {Boolean} valid true if the form has passed client-side validation
6605          */
6606         clientvalidation: true,
6607         /**
6608          * @event beforeaction
6609          * Fires before any action is performed. Return false to cancel the action.
6610          * @param {Form} this
6611          * @param {Action} action The action to be performed
6612          */
6613         beforeaction: true,
6614         /**
6615          * @event actionfailed
6616          * Fires when an action fails.
6617          * @param {Form} this
6618          * @param {Action} action The action that failed
6619          */
6620         actionfailed : true,
6621         /**
6622          * @event actioncomplete
6623          * Fires when an action is completed.
6624          * @param {Form} this
6625          * @param {Action} action The action that completed
6626          */
6627         actioncomplete : true
6628     });
6629     
6630 };
6631
6632 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6633       
6634      /**
6635      * @cfg {String} method
6636      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6637      */
6638     method : 'POST',
6639     /**
6640      * @cfg {String} url
6641      * The URL to use for form actions if one isn't supplied in the action options.
6642      */
6643     /**
6644      * @cfg {Boolean} fileUpload
6645      * Set to true if this form is a file upload.
6646      */
6647      
6648     /**
6649      * @cfg {Object} baseParams
6650      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6651      */
6652       
6653     /**
6654      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6655      */
6656     timeout: 30,
6657     /**
6658      * @cfg {Sting} align (left|right) for navbar forms
6659      */
6660     align : 'left',
6661
6662     // private
6663     activeAction : null,
6664  
6665     /**
6666      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6667      * element by passing it or its id or mask the form itself by passing in true.
6668      * @type Mixed
6669      */
6670     waitMsgTarget : false,
6671     
6672     loadMask : true,
6673     
6674     getAutoCreate : function(){
6675         
6676         var cfg = {
6677             tag: 'form',
6678             method : this.method || 'POST',
6679             id : this.id || Roo.id(),
6680             cls : ''
6681         }
6682         if (this.parent().xtype.match(/^Nav/)) {
6683             cfg.cls = 'navbar-form navbar-' + this.align;
6684             
6685         }
6686         
6687         if (this.labelAlign == 'left' ) {
6688             cfg.cls += ' form-horizontal';
6689         }
6690         
6691         
6692         return cfg;
6693     },
6694     initEvents : function()
6695     {
6696         this.el.on('submit', this.onSubmit, this);
6697         // this was added as random key presses on the form where triggering form submit.
6698         this.el.on('keypress', function(e) {
6699             if (e.getCharCode() != 13) {
6700                 return true;
6701             }
6702             // we might need to allow it for textareas.. and some other items.
6703             // check e.getTarget().
6704             
6705             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6706                 return true;
6707             }
6708         
6709             Roo.log("keypress blocked");
6710             
6711             e.preventDefault();
6712             return false;
6713         });
6714         
6715     },
6716     // private
6717     onSubmit : function(e){
6718         e.stopEvent();
6719     },
6720     
6721      /**
6722      * Returns true if client-side validation on the form is successful.
6723      * @return Boolean
6724      */
6725     isValid : function(){
6726         var items = this.getItems();
6727         var valid = true;
6728         items.each(function(f){
6729            if(!f.validate()){
6730                valid = false;
6731                
6732            }
6733         });
6734         return valid;
6735     },
6736     /**
6737      * Returns true if any fields in this form have changed since their original load.
6738      * @return Boolean
6739      */
6740     isDirty : function(){
6741         var dirty = false;
6742         var items = this.getItems();
6743         items.each(function(f){
6744            if(f.isDirty()){
6745                dirty = true;
6746                return false;
6747            }
6748            return true;
6749         });
6750         return dirty;
6751     },
6752      /**
6753      * Performs a predefined action (submit or load) or custom actions you define on this form.
6754      * @param {String} actionName The name of the action type
6755      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6756      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6757      * accept other config options):
6758      * <pre>
6759 Property          Type             Description
6760 ----------------  ---------------  ----------------------------------------------------------------------------------
6761 url               String           The url for the action (defaults to the form's url)
6762 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6763 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6764 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6765                                    validate the form on the client (defaults to false)
6766      * </pre>
6767      * @return {BasicForm} this
6768      */
6769     doAction : function(action, options){
6770         if(typeof action == 'string'){
6771             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6772         }
6773         if(this.fireEvent('beforeaction', this, action) !== false){
6774             this.beforeAction(action);
6775             action.run.defer(100, action);
6776         }
6777         return this;
6778     },
6779     
6780     // private
6781     beforeAction : function(action){
6782         var o = action.options;
6783         
6784         if(this.loadMask){
6785             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6786         }
6787         // not really supported yet.. ??
6788         
6789         //if(this.waitMsgTarget === true){
6790         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6791         //}else if(this.waitMsgTarget){
6792         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6793         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6794         //}else {
6795         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6796        // }
6797          
6798     },
6799
6800     // private
6801     afterAction : function(action, success){
6802         this.activeAction = null;
6803         var o = action.options;
6804         
6805         //if(this.waitMsgTarget === true){
6806             this.el.unmask();
6807         //}else if(this.waitMsgTarget){
6808         //    this.waitMsgTarget.unmask();
6809         //}else{
6810         //    Roo.MessageBox.updateProgress(1);
6811         //    Roo.MessageBox.hide();
6812        // }
6813         // 
6814         if(success){
6815             if(o.reset){
6816                 this.reset();
6817             }
6818             Roo.callback(o.success, o.scope, [this, action]);
6819             this.fireEvent('actioncomplete', this, action);
6820             
6821         }else{
6822             
6823             // failure condition..
6824             // we have a scenario where updates need confirming.
6825             // eg. if a locking scenario exists..
6826             // we look for { errors : { needs_confirm : true }} in the response.
6827             if (
6828                 (typeof(action.result) != 'undefined')  &&
6829                 (typeof(action.result.errors) != 'undefined')  &&
6830                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6831            ){
6832                 var _t = this;
6833                 Roo.log("not supported yet");
6834                  /*
6835                 
6836                 Roo.MessageBox.confirm(
6837                     "Change requires confirmation",
6838                     action.result.errorMsg,
6839                     function(r) {
6840                         if (r != 'yes') {
6841                             return;
6842                         }
6843                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6844                     }
6845                     
6846                 );
6847                 */
6848                 
6849                 
6850                 return;
6851             }
6852             
6853             Roo.callback(o.failure, o.scope, [this, action]);
6854             // show an error message if no failed handler is set..
6855             if (!this.hasListener('actionfailed')) {
6856                 Roo.log("need to add dialog support");
6857                 /*
6858                 Roo.MessageBox.alert("Error",
6859                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6860                         action.result.errorMsg :
6861                         "Saving Failed, please check your entries or try again"
6862                 );
6863                 */
6864             }
6865             
6866             this.fireEvent('actionfailed', this, action);
6867         }
6868         
6869     },
6870     /**
6871      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6872      * @param {String} id The value to search for
6873      * @return Field
6874      */
6875     findField : function(id){
6876         var items = this.getItems();
6877         var field = items.get(id);
6878         if(!field){
6879              items.each(function(f){
6880                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6881                     field = f;
6882                     return false;
6883                 }
6884                 return true;
6885             });
6886         }
6887         return field || null;
6888     },
6889      /**
6890      * Mark fields in this form invalid in bulk.
6891      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6892      * @return {BasicForm} this
6893      */
6894     markInvalid : function(errors){
6895         if(errors instanceof Array){
6896             for(var i = 0, len = errors.length; i < len; i++){
6897                 var fieldError = errors[i];
6898                 var f = this.findField(fieldError.id);
6899                 if(f){
6900                     f.markInvalid(fieldError.msg);
6901                 }
6902             }
6903         }else{
6904             var field, id;
6905             for(id in errors){
6906                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6907                     field.markInvalid(errors[id]);
6908                 }
6909             }
6910         }
6911         //Roo.each(this.childForms || [], function (f) {
6912         //    f.markInvalid(errors);
6913         //});
6914         
6915         return this;
6916     },
6917
6918     /**
6919      * Set values for fields in this form in bulk.
6920      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6921      * @return {BasicForm} this
6922      */
6923     setValues : function(values){
6924         if(values instanceof Array){ // array of objects
6925             for(var i = 0, len = values.length; i < len; i++){
6926                 var v = values[i];
6927                 var f = this.findField(v.id);
6928                 if(f){
6929                     f.setValue(v.value);
6930                     if(this.trackResetOnLoad){
6931                         f.originalValue = f.getValue();
6932                     }
6933                 }
6934             }
6935         }else{ // object hash
6936             var field, id;
6937             for(id in values){
6938                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6939                     
6940                     if (field.setFromData && 
6941                         field.valueField && 
6942                         field.displayField &&
6943                         // combos' with local stores can 
6944                         // be queried via setValue()
6945                         // to set their value..
6946                         (field.store && !field.store.isLocal)
6947                         ) {
6948                         // it's a combo
6949                         var sd = { };
6950                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6951                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6952                         field.setFromData(sd);
6953                         
6954                     } else {
6955                         field.setValue(values[id]);
6956                     }
6957                     
6958                     
6959                     if(this.trackResetOnLoad){
6960                         field.originalValue = field.getValue();
6961                     }
6962                 }
6963             }
6964         }
6965          
6966         //Roo.each(this.childForms || [], function (f) {
6967         //    f.setValues(values);
6968         //});
6969                 
6970         return this;
6971     },
6972
6973     /**
6974      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6975      * they are returned as an array.
6976      * @param {Boolean} asString
6977      * @return {Object}
6978      */
6979     getValues : function(asString){
6980         //if (this.childForms) {
6981             // copy values from the child forms
6982         //    Roo.each(this.childForms, function (f) {
6983         //        this.setValues(f.getValues());
6984         //    }, this);
6985         //}
6986         
6987         
6988         
6989         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6990         if(asString === true){
6991             return fs;
6992         }
6993         return Roo.urlDecode(fs);
6994     },
6995     
6996     /**
6997      * Returns the fields in this form as an object with key/value pairs. 
6998      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6999      * @return {Object}
7000      */
7001     getFieldValues : function(with_hidden)
7002     {
7003         var items = this.getItems();
7004         var ret = {};
7005         items.each(function(f){
7006             if (!f.getName()) {
7007                 return;
7008             }
7009             var v = f.getValue();
7010             if (f.inputType =='radio') {
7011                 if (typeof(ret[f.getName()]) == 'undefined') {
7012                     ret[f.getName()] = ''; // empty..
7013                 }
7014                 
7015                 if (!f.el.dom.checked) {
7016                     return;
7017                     
7018                 }
7019                 v = f.el.dom.value;
7020                 
7021             }
7022             
7023             // not sure if this supported any more..
7024             if ((typeof(v) == 'object') && f.getRawValue) {
7025                 v = f.getRawValue() ; // dates..
7026             }
7027             // combo boxes where name != hiddenName...
7028             if (f.name != f.getName()) {
7029                 ret[f.name] = f.getRawValue();
7030             }
7031             ret[f.getName()] = v;
7032         });
7033         
7034         return ret;
7035     },
7036
7037     /**
7038      * Clears all invalid messages in this form.
7039      * @return {BasicForm} this
7040      */
7041     clearInvalid : function(){
7042         var items = this.getItems();
7043         
7044         items.each(function(f){
7045            f.clearInvalid();
7046         });
7047         
7048         
7049         
7050         return this;
7051     },
7052
7053     /**
7054      * Resets this form.
7055      * @return {BasicForm} this
7056      */
7057     reset : function(){
7058         var items = this.getItems();
7059         items.each(function(f){
7060             f.reset();
7061         });
7062         
7063         Roo.each(this.childForms || [], function (f) {
7064             f.reset();
7065         });
7066        
7067         
7068         return this;
7069     },
7070     getItems : function()
7071     {
7072         var r=new Roo.util.MixedCollection(false, function(o){
7073             return o.id || (o.id = Roo.id());
7074         });
7075         var iter = function(el) {
7076             if (el.inputEl) {
7077                 r.add(el);
7078             }
7079             if (!el.items) {
7080                 return;
7081             }
7082             Roo.each(el.items,function(e) {
7083                 iter(e);
7084             });
7085             
7086             
7087         };
7088         
7089         iter(this);
7090         return r;
7091         
7092         
7093         
7094         
7095     }
7096     
7097 });
7098
7099  
7100 /*
7101  * Based on:
7102  * Ext JS Library 1.1.1
7103  * Copyright(c) 2006-2007, Ext JS, LLC.
7104  *
7105  * Originally Released Under LGPL - original licence link has changed is not relivant.
7106  *
7107  * Fork - LGPL
7108  * <script type="text/javascript">
7109  */
7110 /**
7111  * @class Roo.form.VTypes
7112  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7113  * @singleton
7114  */
7115 Roo.form.VTypes = function(){
7116     // closure these in so they are only created once.
7117     var alpha = /^[a-zA-Z_]+$/;
7118     var alphanum = /^[a-zA-Z0-9_]+$/;
7119     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7120     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7121
7122     // All these messages and functions are configurable
7123     return {
7124         /**
7125          * The function used to validate email addresses
7126          * @param {String} value The email address
7127          */
7128         'email' : function(v){
7129             return email.test(v);
7130         },
7131         /**
7132          * The error text to display when the email validation function returns false
7133          * @type String
7134          */
7135         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7136         /**
7137          * The keystroke filter mask to be applied on email input
7138          * @type RegExp
7139          */
7140         'emailMask' : /[a-z0-9_\.\-@]/i,
7141
7142         /**
7143          * The function used to validate URLs
7144          * @param {String} value The URL
7145          */
7146         'url' : function(v){
7147             return url.test(v);
7148         },
7149         /**
7150          * The error text to display when the url validation function returns false
7151          * @type String
7152          */
7153         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7154         
7155         /**
7156          * The function used to validate alpha values
7157          * @param {String} value The value
7158          */
7159         'alpha' : function(v){
7160             return alpha.test(v);
7161         },
7162         /**
7163          * The error text to display when the alpha validation function returns false
7164          * @type String
7165          */
7166         'alphaText' : 'This field should only contain letters and _',
7167         /**
7168          * The keystroke filter mask to be applied on alpha input
7169          * @type RegExp
7170          */
7171         'alphaMask' : /[a-z_]/i,
7172
7173         /**
7174          * The function used to validate alphanumeric values
7175          * @param {String} value The value
7176          */
7177         'alphanum' : function(v){
7178             return alphanum.test(v);
7179         },
7180         /**
7181          * The error text to display when the alphanumeric validation function returns false
7182          * @type String
7183          */
7184         'alphanumText' : 'This field should only contain letters, numbers and _',
7185         /**
7186          * The keystroke filter mask to be applied on alphanumeric input
7187          * @type RegExp
7188          */
7189         'alphanumMask' : /[a-z0-9_]/i
7190     };
7191 }();/*
7192  * - LGPL
7193  *
7194  * Input
7195  * 
7196  */
7197
7198 /**
7199  * @class Roo.bootstrap.Input
7200  * @extends Roo.bootstrap.Component
7201  * Bootstrap Input class
7202  * @cfg {Boolean} disabled is it disabled
7203  * @cfg {String} fieldLabel - the label associated
7204  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7205  * @cfg {String} name name of the input
7206  * @cfg {string} fieldLabel - the label associated
7207  * @cfg {string}  inputType - input / file submit ...
7208  * @cfg {string} placeholder - placeholder to put in text.
7209  * @cfg {string}  before - input group add on before
7210  * @cfg {string} after - input group add on after
7211  * @cfg {string} size - (lg|sm) or leave empty..
7212  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7213  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7214  * @cfg {Number} md colspan out of 12 for computer-sized screens
7215  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7216  * @cfg {string} value default value of the input
7217  * @cfg {Number} labelWidth set the width of label (0-12)
7218  * @cfg {String} labelAlign (top|left)
7219  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7220  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7221
7222  * @cfg {String} align (left|center|right) Default left
7223  * 
7224  * 
7225  * 
7226  * @constructor
7227  * Create a new Input
7228  * @param {Object} config The config object
7229  */
7230
7231 Roo.bootstrap.Input = function(config){
7232     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7233    
7234         this.addEvents({
7235             /**
7236              * @event focus
7237              * Fires when this field receives input focus.
7238              * @param {Roo.form.Field} this
7239              */
7240             focus : true,
7241             /**
7242              * @event blur
7243              * Fires when this field loses input focus.
7244              * @param {Roo.form.Field} this
7245              */
7246             blur : true,
7247             /**
7248              * @event specialkey
7249              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7250              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7251              * @param {Roo.form.Field} this
7252              * @param {Roo.EventObject} e The event object
7253              */
7254             specialkey : true,
7255             /**
7256              * @event change
7257              * Fires just before the field blurs if the field value has changed.
7258              * @param {Roo.form.Field} this
7259              * @param {Mixed} newValue The new value
7260              * @param {Mixed} oldValue The original value
7261              */
7262             change : true,
7263             /**
7264              * @event invalid
7265              * Fires after the field has been marked as invalid.
7266              * @param {Roo.form.Field} this
7267              * @param {String} msg The validation message
7268              */
7269             invalid : true,
7270             /**
7271              * @event valid
7272              * Fires after the field has been validated with no errors.
7273              * @param {Roo.form.Field} this
7274              */
7275             valid : true,
7276              /**
7277              * @event keyup
7278              * Fires after the key up
7279              * @param {Roo.form.Field} this
7280              * @param {Roo.EventObject}  e The event Object
7281              */
7282             keyup : true
7283         });
7284 };
7285
7286 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7287      /**
7288      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7289       automatic validation (defaults to "keyup").
7290      */
7291     validationEvent : "keyup",
7292      /**
7293      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7294      */
7295     validateOnBlur : true,
7296     /**
7297      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7298      */
7299     validationDelay : 250,
7300      /**
7301      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7302      */
7303     focusClass : "x-form-focus",  // not needed???
7304     
7305        
7306     /**
7307      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7308      */
7309     invalidClass : "has-warning",
7310     
7311     /**
7312      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7313      */
7314     validClass : "has-success",
7315     
7316     /**
7317      * @cfg {Boolean} hasFeedback (true|false) default true
7318      */
7319     hasFeedback : true,
7320     
7321     /**
7322      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7323      */
7324     invalidFeedbackClass : "glyphicon-warning-sign",
7325     
7326     /**
7327      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7328      */
7329     validFeedbackClass : "glyphicon-ok",
7330     
7331     /**
7332      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7333      */
7334     selectOnFocus : false,
7335     
7336      /**
7337      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7338      */
7339     maskRe : null,
7340        /**
7341      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7342      */
7343     vtype : null,
7344     
7345       /**
7346      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7347      */
7348     disableKeyFilter : false,
7349     
7350        /**
7351      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7352      */
7353     disabled : false,
7354      /**
7355      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7356      */
7357     allowBlank : true,
7358     /**
7359      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7360      */
7361     blankText : "This field is required",
7362     
7363      /**
7364      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7365      */
7366     minLength : 0,
7367     /**
7368      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7369      */
7370     maxLength : Number.MAX_VALUE,
7371     /**
7372      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7373      */
7374     minLengthText : "The minimum length for this field is {0}",
7375     /**
7376      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7377      */
7378     maxLengthText : "The maximum length for this field is {0}",
7379   
7380     
7381     /**
7382      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7383      * If available, this function will be called only after the basic validators all return true, and will be passed the
7384      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7385      */
7386     validator : null,
7387     /**
7388      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7389      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7390      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7391      */
7392     regex : null,
7393     /**
7394      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7395      */
7396     regexText : "",
7397     
7398     autocomplete: false,
7399     
7400     
7401     fieldLabel : '',
7402     inputType : 'text',
7403     
7404     name : false,
7405     placeholder: false,
7406     before : false,
7407     after : false,
7408     size : false,
7409     hasFocus : false,
7410     preventMark: false,
7411     isFormField : true,
7412     value : '',
7413     labelWidth : 2,
7414     labelAlign : false,
7415     readOnly : false,
7416     align : false,
7417     formatedValue : false,
7418     
7419     parentLabelAlign : function()
7420     {
7421         var parent = this;
7422         while (parent.parent()) {
7423             parent = parent.parent();
7424             if (typeof(parent.labelAlign) !='undefined') {
7425                 return parent.labelAlign;
7426             }
7427         }
7428         return 'left';
7429         
7430     },
7431     
7432     getAutoCreate : function(){
7433         
7434         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7435         
7436         var id = Roo.id();
7437         
7438         var cfg = {};
7439         
7440         if(this.inputType != 'hidden'){
7441             cfg.cls = 'form-group' //input-group
7442         }
7443         
7444         var input =  {
7445             tag: 'input',
7446             id : id,
7447             type : this.inputType,
7448             value : this.value,
7449             cls : 'form-control',
7450             placeholder : this.placeholder || '',
7451             autocomplete : this.autocomplete || 'new-password'
7452         };
7453         
7454         
7455         if(this.align){
7456             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7457         }
7458         
7459         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7460             input.maxLength = this.maxLength;
7461         }
7462         
7463         if (this.disabled) {
7464             input.disabled=true;
7465         }
7466         
7467         if (this.readOnly) {
7468             input.readonly=true;
7469         }
7470         
7471         if (this.name) {
7472             input.name = this.name;
7473         }
7474         if (this.size) {
7475             input.cls += ' input-' + this.size;
7476         }
7477         var settings=this;
7478         ['xs','sm','md','lg'].map(function(size){
7479             if (settings[size]) {
7480                 cfg.cls += ' col-' + size + '-' + settings[size];
7481             }
7482         });
7483         
7484         var inputblock = input;
7485         
7486         var feedback = {
7487             tag: 'span',
7488             cls: 'glyphicon form-control-feedback'
7489         };
7490             
7491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7492             
7493             inputblock = {
7494                 cls : 'has-feedback',
7495                 cn :  [
7496                     input,
7497                     feedback
7498                 ] 
7499             };  
7500         }
7501         
7502         if (this.before || this.after) {
7503             
7504             inputblock = {
7505                 cls : 'input-group',
7506                 cn :  [] 
7507             };
7508             
7509             if (this.before && typeof(this.before) == 'string') {
7510                 
7511                 inputblock.cn.push({
7512                     tag :'span',
7513                     cls : 'roo-input-before input-group-addon',
7514                     html : this.before
7515                 });
7516             }
7517             if (this.before && typeof(this.before) == 'object') {
7518                 this.before = Roo.factory(this.before);
7519                 Roo.log(this.before);
7520                 inputblock.cn.push({
7521                     tag :'span',
7522                     cls : 'roo-input-before input-group-' +
7523                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7524                 });
7525             }
7526             
7527             inputblock.cn.push(input);
7528             
7529             if (this.after && typeof(this.after) == 'string') {
7530                 inputblock.cn.push({
7531                     tag :'span',
7532                     cls : 'roo-input-after input-group-addon',
7533                     html : this.after
7534                 });
7535             }
7536             if (this.after && typeof(this.after) == 'object') {
7537                 this.after = Roo.factory(this.after);
7538                 Roo.log(this.after);
7539                 inputblock.cn.push({
7540                     tag :'span',
7541                     cls : 'roo-input-after input-group-' +
7542                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7543                 });
7544             }
7545             
7546             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7547                 inputblock.cls += ' has-feedback';
7548                 inputblock.cn.push(feedback);
7549             }
7550         };
7551         
7552         if (align ==='left' && this.fieldLabel.length) {
7553                 Roo.log("left and has label");
7554                 cfg.cn = [
7555                     
7556                     {
7557                         tag: 'label',
7558                         'for' :  id,
7559                         cls : 'control-label col-sm-' + this.labelWidth,
7560                         html : this.fieldLabel
7561                         
7562                     },
7563                     {
7564                         cls : "col-sm-" + (12 - this.labelWidth), 
7565                         cn: [
7566                             inputblock
7567                         ]
7568                     }
7569                     
7570                 ];
7571         } else if ( this.fieldLabel.length) {
7572                 Roo.log(" label");
7573                  cfg.cn = [
7574                    
7575                     {
7576                         tag: 'label',
7577                         //cls : 'input-group-addon',
7578                         html : this.fieldLabel
7579                         
7580                     },
7581                     
7582                     inputblock
7583                     
7584                 ];
7585
7586         } else {
7587             
7588                 Roo.log(" no label && no align");
7589                 cfg.cn = [
7590                     
7591                         inputblock
7592                     
7593                 ];
7594                 
7595                 
7596         };
7597         Roo.log('input-parentType: ' + this.parentType);
7598         
7599         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7600            cfg.cls += ' navbar-form';
7601            Roo.log(cfg);
7602         }
7603         
7604         return cfg;
7605         
7606     },
7607     /**
7608      * return the real input element.
7609      */
7610     inputEl: function ()
7611     {
7612         return this.el.select('input.form-control',true).first();
7613     },
7614     
7615     tooltipEl : function()
7616     {
7617         return this.inputEl();
7618     },
7619     
7620     setDisabled : function(v)
7621     {
7622         var i  = this.inputEl().dom;
7623         if (!v) {
7624             i.removeAttribute('disabled');
7625             return;
7626             
7627         }
7628         i.setAttribute('disabled','true');
7629     },
7630     initEvents : function()
7631     {
7632           
7633         this.inputEl().on("keydown" , this.fireKey,  this);
7634         this.inputEl().on("focus", this.onFocus,  this);
7635         this.inputEl().on("blur", this.onBlur,  this);
7636         
7637         this.inputEl().relayEvent('keyup', this);
7638
7639         // reference to original value for reset
7640         this.originalValue = this.getValue();
7641         //Roo.form.TextField.superclass.initEvents.call(this);
7642         if(this.validationEvent == 'keyup'){
7643             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7644             this.inputEl().on('keyup', this.filterValidation, this);
7645         }
7646         else if(this.validationEvent !== false){
7647             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7648         }
7649         
7650         if(this.selectOnFocus){
7651             this.on("focus", this.preFocus, this);
7652             
7653         }
7654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7655             this.inputEl().on("keypress", this.filterKeys, this);
7656         }
7657        /* if(this.grow){
7658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7659             this.el.on("click", this.autoSize,  this);
7660         }
7661         */
7662         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7663             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7664         }
7665         
7666         if (typeof(this.before) == 'object') {
7667             this.before.render(this.el.select('.roo-input-before',true).first());
7668         }
7669         if (typeof(this.after) == 'object') {
7670             this.after.render(this.el.select('.roo-input-after',true).first());
7671         }
7672         
7673         
7674     },
7675     filterValidation : function(e){
7676         if(!e.isNavKeyPress()){
7677             this.validationTask.delay(this.validationDelay);
7678         }
7679     },
7680      /**
7681      * Validates the field value
7682      * @return {Boolean} True if the value is valid, else false
7683      */
7684     validate : function(){
7685         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7686         if(this.disabled || this.validateValue(this.getRawValue())){
7687             this.markValid();
7688             return true;
7689         }
7690         
7691         this.markInvalid();
7692         return false;
7693     },
7694     
7695     
7696     /**
7697      * Validates a value according to the field's validation rules and marks the field as invalid
7698      * if the validation fails
7699      * @param {Mixed} value The value to validate
7700      * @return {Boolean} True if the value is valid, else false
7701      */
7702     validateValue : function(value){
7703         if(value.length < 1)  { // if it's blank
7704             if(this.allowBlank){
7705                 return true;
7706             }
7707             return false;
7708         }
7709         
7710         if(value.length < this.minLength){
7711             return false;
7712         }
7713         if(value.length > this.maxLength){
7714             return false;
7715         }
7716         if(this.vtype){
7717             var vt = Roo.form.VTypes;
7718             if(!vt[this.vtype](value, this)){
7719                 return false;
7720             }
7721         }
7722         if(typeof this.validator == "function"){
7723             var msg = this.validator(value);
7724             if(msg !== true){
7725                 return false;
7726             }
7727         }
7728         
7729         if(this.regex && !this.regex.test(value)){
7730             return false;
7731         }
7732         
7733         return true;
7734     },
7735
7736     
7737     
7738      // private
7739     fireKey : function(e){
7740         //Roo.log('field ' + e.getKey());
7741         if(e.isNavKeyPress()){
7742             this.fireEvent("specialkey", this, e);
7743         }
7744     },
7745     focus : function (selectText){
7746         if(this.rendered){
7747             this.inputEl().focus();
7748             if(selectText === true){
7749                 this.inputEl().dom.select();
7750             }
7751         }
7752         return this;
7753     } ,
7754     
7755     onFocus : function(){
7756         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7757            // this.el.addClass(this.focusClass);
7758         }
7759         if(!this.hasFocus){
7760             this.hasFocus = true;
7761             this.startValue = this.getValue();
7762             this.fireEvent("focus", this);
7763         }
7764     },
7765     
7766     beforeBlur : Roo.emptyFn,
7767
7768     
7769     // private
7770     onBlur : function(){
7771         this.beforeBlur();
7772         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7773             //this.el.removeClass(this.focusClass);
7774         }
7775         this.hasFocus = false;
7776         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7777             this.validate();
7778         }
7779         var v = this.getValue();
7780         if(String(v) !== String(this.startValue)){
7781             this.fireEvent('change', this, v, this.startValue);
7782         }
7783         this.fireEvent("blur", this);
7784     },
7785     
7786     /**
7787      * Resets the current field value to the originally loaded value and clears any validation messages
7788      */
7789     reset : function(){
7790         this.setValue(this.originalValue);
7791         this.validate();
7792     },
7793      /**
7794      * Returns the name of the field
7795      * @return {Mixed} name The name field
7796      */
7797     getName: function(){
7798         return this.name;
7799     },
7800      /**
7801      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7802      * @return {Mixed} value The field value
7803      */
7804     getValue : function(){
7805         
7806         var v = this.inputEl().getValue();
7807         
7808         return v;
7809     },
7810     /**
7811      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7812      * @return {Mixed} value The field value
7813      */
7814     getRawValue : function(){
7815         var v = this.inputEl().getValue();
7816         
7817         return v;
7818     },
7819     
7820     /**
7821      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7822      * @param {Mixed} value The value to set
7823      */
7824     setRawValue : function(v){
7825         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7826     },
7827     
7828     selectText : function(start, end){
7829         var v = this.getRawValue();
7830         if(v.length > 0){
7831             start = start === undefined ? 0 : start;
7832             end = end === undefined ? v.length : end;
7833             var d = this.inputEl().dom;
7834             if(d.setSelectionRange){
7835                 d.setSelectionRange(start, end);
7836             }else if(d.createTextRange){
7837                 var range = d.createTextRange();
7838                 range.moveStart("character", start);
7839                 range.moveEnd("character", v.length-end);
7840                 range.select();
7841             }
7842         }
7843     },
7844     
7845     /**
7846      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7847      * @param {Mixed} value The value to set
7848      */
7849     setValue : function(v){
7850         this.value = v;
7851         if(this.rendered){
7852             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7853             this.validate();
7854         }
7855     },
7856     
7857     /*
7858     processValue : function(value){
7859         if(this.stripCharsRe){
7860             var newValue = value.replace(this.stripCharsRe, '');
7861             if(newValue !== value){
7862                 this.setRawValue(newValue);
7863                 return newValue;
7864             }
7865         }
7866         return value;
7867     },
7868   */
7869     preFocus : function(){
7870         
7871         if(this.selectOnFocus){
7872             this.inputEl().dom.select();
7873         }
7874     },
7875     filterKeys : function(e){
7876         var k = e.getKey();
7877         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7878             return;
7879         }
7880         var c = e.getCharCode(), cc = String.fromCharCode(c);
7881         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7882             return;
7883         }
7884         if(!this.maskRe.test(cc)){
7885             e.stopEvent();
7886         }
7887     },
7888      /**
7889      * Clear any invalid styles/messages for this field
7890      */
7891     clearInvalid : function(){
7892         
7893         if(!this.el || this.preventMark){ // not rendered
7894             return;
7895         }
7896         this.el.removeClass(this.invalidClass);
7897         
7898         this.fireEvent('valid', this);
7899     },
7900     
7901      /**
7902      * Mark this field as valid
7903      */
7904     markValid : function(){
7905         if(!this.el  || this.preventMark){ // not rendered
7906             return;
7907         }
7908         
7909         this.el.removeClass([this.invalidClass, this.validClass]);
7910         
7911         if(this.disabled || this.allowBlank){
7912             return;
7913         }
7914         
7915         this.el.addClass(this.validClass);
7916         
7917         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7918             
7919             var feedback = this.el.select('.form-control-feedback', true).first();
7920             
7921             if(feedback){
7922                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7923                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7924             }
7925             
7926         }
7927         
7928         this.fireEvent('valid', this);
7929     },
7930     
7931      /**
7932      * Mark this field as invalid
7933      * @param {String} msg The validation message
7934      */
7935     markInvalid : function(msg){
7936         if(!this.el  || this.preventMark){ // not rendered
7937             return;
7938         }
7939         
7940         this.el.removeClass([this.invalidClass, this.validClass]);
7941         
7942         if(this.disabled || this.allowBlank){
7943             return;
7944         }
7945         
7946         this.el.addClass(this.invalidClass);
7947         
7948         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7949             
7950             var feedback = this.el.select('.form-control-feedback', true).first();
7951             
7952             if(feedback){
7953                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7954                 
7955                 if(this.getValue().length){
7956                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7957                 }
7958                 
7959             }
7960             
7961         }
7962         
7963         this.fireEvent('invalid', this, msg);
7964     },
7965     // private
7966     SafariOnKeyDown : function(event)
7967     {
7968         // this is a workaround for a password hang bug on chrome/ webkit.
7969         
7970         var isSelectAll = false;
7971         
7972         if(this.inputEl().dom.selectionEnd > 0){
7973             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7974         }
7975         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7976             event.preventDefault();
7977             this.setValue('');
7978             return;
7979         }
7980         
7981         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7982             
7983             event.preventDefault();
7984             // this is very hacky as keydown always get's upper case.
7985             //
7986             var cc = String.fromCharCode(event.getCharCode());
7987             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7988             
7989         }
7990     },
7991     adjustWidth : function(tag, w){
7992         tag = tag.toLowerCase();
7993         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7994             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7995                 if(tag == 'input'){
7996                     return w + 2;
7997                 }
7998                 if(tag == 'textarea'){
7999                     return w-2;
8000                 }
8001             }else if(Roo.isOpera){
8002                 if(tag == 'input'){
8003                     return w + 2;
8004                 }
8005                 if(tag == 'textarea'){
8006                     return w-2;
8007                 }
8008             }
8009         }
8010         return w;
8011     }
8012     
8013 });
8014
8015  
8016 /*
8017  * - LGPL
8018  *
8019  * Input
8020  * 
8021  */
8022
8023 /**
8024  * @class Roo.bootstrap.TextArea
8025  * @extends Roo.bootstrap.Input
8026  * Bootstrap TextArea class
8027  * @cfg {Number} cols Specifies the visible width of a text area
8028  * @cfg {Number} rows Specifies the visible number of lines in a text area
8029  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8030  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8031  * @cfg {string} html text
8032  * 
8033  * @constructor
8034  * Create a new TextArea
8035  * @param {Object} config The config object
8036  */
8037
8038 Roo.bootstrap.TextArea = function(config){
8039     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8040    
8041 };
8042
8043 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8044      
8045     cols : false,
8046     rows : 5,
8047     readOnly : false,
8048     warp : 'soft',
8049     resize : false,
8050     value: false,
8051     html: false,
8052     
8053     getAutoCreate : function(){
8054         
8055         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8056         
8057         var id = Roo.id();
8058         
8059         var cfg = {};
8060         
8061         var input =  {
8062             tag: 'textarea',
8063             id : id,
8064             warp : this.warp,
8065             rows : this.rows,
8066             value : this.value || '',
8067             html: this.html || '',
8068             cls : 'form-control',
8069             placeholder : this.placeholder || '' 
8070             
8071         };
8072         
8073         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8074             input.maxLength = this.maxLength;
8075         }
8076         
8077         if(this.resize){
8078             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8079         }
8080         
8081         if(this.cols){
8082             input.cols = this.cols;
8083         }
8084         
8085         if (this.readOnly) {
8086             input.readonly = true;
8087         }
8088         
8089         if (this.name) {
8090             input.name = this.name;
8091         }
8092         
8093         if (this.size) {
8094             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8095         }
8096         
8097         var settings=this;
8098         ['xs','sm','md','lg'].map(function(size){
8099             if (settings[size]) {
8100                 cfg.cls += ' col-' + size + '-' + settings[size];
8101             }
8102         });
8103         
8104         var inputblock = input;
8105         
8106         if(this.hasFeedback && !this.allowBlank){
8107             
8108             var feedback = {
8109                 tag: 'span',
8110                 cls: 'glyphicon form-control-feedback'
8111             };
8112
8113             inputblock = {
8114                 cls : 'has-feedback',
8115                 cn :  [
8116                     input,
8117                     feedback
8118                 ] 
8119             };  
8120         }
8121         
8122         
8123         if (this.before || this.after) {
8124             
8125             inputblock = {
8126                 cls : 'input-group',
8127                 cn :  [] 
8128             };
8129             if (this.before) {
8130                 inputblock.cn.push({
8131                     tag :'span',
8132                     cls : 'input-group-addon',
8133                     html : this.before
8134                 });
8135             }
8136             
8137             inputblock.cn.push(input);
8138             
8139             if(this.hasFeedback && !this.allowBlank){
8140                 inputblock.cls += ' has-feedback';
8141                 inputblock.cn.push(feedback);
8142             }
8143             
8144             if (this.after) {
8145                 inputblock.cn.push({
8146                     tag :'span',
8147                     cls : 'input-group-addon',
8148                     html : this.after
8149                 });
8150             }
8151             
8152         }
8153         
8154         if (align ==='left' && this.fieldLabel.length) {
8155                 Roo.log("left and has label");
8156                 cfg.cn = [
8157                     
8158                     {
8159                         tag: 'label',
8160                         'for' :  id,
8161                         cls : 'control-label col-sm-' + this.labelWidth,
8162                         html : this.fieldLabel
8163                         
8164                     },
8165                     {
8166                         cls : "col-sm-" + (12 - this.labelWidth), 
8167                         cn: [
8168                             inputblock
8169                         ]
8170                     }
8171                     
8172                 ];
8173         } else if ( this.fieldLabel.length) {
8174                 Roo.log(" label");
8175                  cfg.cn = [
8176                    
8177                     {
8178                         tag: 'label',
8179                         //cls : 'input-group-addon',
8180                         html : this.fieldLabel
8181                         
8182                     },
8183                     
8184                     inputblock
8185                     
8186                 ];
8187
8188         } else {
8189             
8190                    Roo.log(" no label && no align");
8191                 cfg.cn = [
8192                     
8193                         inputblock
8194                     
8195                 ];
8196                 
8197                 
8198         }
8199         
8200         if (this.disabled) {
8201             input.disabled=true;
8202         }
8203         
8204         return cfg;
8205         
8206     },
8207     /**
8208      * return the real textarea element.
8209      */
8210     inputEl: function ()
8211     {
8212         return this.el.select('textarea.form-control',true).first();
8213     }
8214 });
8215
8216  
8217 /*
8218  * - LGPL
8219  *
8220  * trigger field - base class for combo..
8221  * 
8222  */
8223  
8224 /**
8225  * @class Roo.bootstrap.TriggerField
8226  * @extends Roo.bootstrap.Input
8227  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8228  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8229  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8230  * for which you can provide a custom implementation.  For example:
8231  * <pre><code>
8232 var trigger = new Roo.bootstrap.TriggerField();
8233 trigger.onTriggerClick = myTriggerFn;
8234 trigger.applyTo('my-field');
8235 </code></pre>
8236  *
8237  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8238  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8239  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8240  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8241  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8242
8243  * @constructor
8244  * Create a new TriggerField.
8245  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8246  * to the base TextField)
8247  */
8248 Roo.bootstrap.TriggerField = function(config){
8249     this.mimicing = false;
8250     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8251 };
8252
8253 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8254     /**
8255      * @cfg {String} triggerClass A CSS class to apply to the trigger
8256      */
8257      /**
8258      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8259      */
8260     hideTrigger:false,
8261
8262     /** @cfg {Boolean} grow @hide */
8263     /** @cfg {Number} growMin @hide */
8264     /** @cfg {Number} growMax @hide */
8265
8266     /**
8267      * @hide 
8268      * @method
8269      */
8270     autoSize: Roo.emptyFn,
8271     // private
8272     monitorTab : true,
8273     // private
8274     deferHeight : true,
8275
8276     
8277     actionMode : 'wrap',
8278     
8279     caret : false,
8280     
8281     
8282     getAutoCreate : function(){
8283        
8284         var align = this.labelAlign || this.parentLabelAlign();
8285         
8286         var id = Roo.id();
8287         
8288         var cfg = {
8289             cls: 'form-group' //input-group
8290         };
8291         
8292         
8293         var input =  {
8294             tag: 'input',
8295             id : id,
8296             type : this.inputType,
8297             cls : 'form-control',
8298             autocomplete: 'new-password',
8299             placeholder : this.placeholder || '' 
8300             
8301         };
8302         if (this.name) {
8303             input.name = this.name;
8304         }
8305         if (this.size) {
8306             input.cls += ' input-' + this.size;
8307         }
8308         
8309         if (this.disabled) {
8310             input.disabled=true;
8311         }
8312         
8313         var inputblock = input;
8314         
8315         if(this.hasFeedback && !this.allowBlank){
8316             
8317             var feedback = {
8318                 tag: 'span',
8319                 cls: 'glyphicon form-control-feedback'
8320             };
8321
8322             inputblock = {
8323                 cls : 'has-feedback',
8324                 cn :  [
8325                     input,
8326                     feedback
8327                 ] 
8328             };  
8329         }
8330         
8331         if (this.before || this.after) {
8332             
8333             inputblock = {
8334                 cls : 'input-group',
8335                 cn :  [] 
8336             };
8337             if (this.before) {
8338                 inputblock.cn.push({
8339                     tag :'span',
8340                     cls : 'input-group-addon',
8341                     html : this.before
8342                 });
8343             }
8344             
8345             inputblock.cn.push(input);
8346             
8347             if(this.hasFeedback && !this.allowBlank){
8348                 inputblock.cls += ' has-feedback';
8349                 inputblock.cn.push(feedback);
8350             }
8351             
8352             if (this.after) {
8353                 inputblock.cn.push({
8354                     tag :'span',
8355                     cls : 'input-group-addon',
8356                     html : this.after
8357                 });
8358             }
8359             
8360         };
8361         
8362         var box = {
8363             tag: 'div',
8364             cn: [
8365                 {
8366                     tag: 'input',
8367                     type : 'hidden',
8368                     cls: 'form-hidden-field'
8369                 },
8370                 inputblock
8371             ]
8372             
8373         };
8374         
8375         if(this.multiple){
8376             Roo.log('multiple');
8377             
8378             box = {
8379                 tag: 'div',
8380                 cn: [
8381                     {
8382                         tag: 'input',
8383                         type : 'hidden',
8384                         cls: 'form-hidden-field'
8385                     },
8386                     {
8387                         tag: 'ul',
8388                         cls: 'select2-choices',
8389                         cn:[
8390                             {
8391                                 tag: 'li',
8392                                 cls: 'select2-search-field',
8393                                 cn: [
8394
8395                                     inputblock
8396                                 ]
8397                             }
8398                         ]
8399                     }
8400                 ]
8401             }
8402         };
8403         
8404         var combobox = {
8405             cls: 'select2-container input-group',
8406             cn: [
8407                 box
8408 //                {
8409 //                    tag: 'ul',
8410 //                    cls: 'typeahead typeahead-long dropdown-menu',
8411 //                    style: 'display:none'
8412 //                }
8413             ]
8414         };
8415         
8416         if(!this.multiple && this.showToggleBtn){
8417             
8418             var caret = {
8419                         tag: 'span',
8420                         cls: 'caret'
8421              };
8422             if (this.caret != false) {
8423                 caret = {
8424                      tag: 'i',
8425                      cls: 'fa fa-' + this.caret
8426                 };
8427                 
8428             }
8429             
8430             combobox.cn.push({
8431                 tag :'span',
8432                 cls : 'input-group-addon btn dropdown-toggle',
8433                 cn : [
8434                     caret,
8435                     {
8436                         tag: 'span',
8437                         cls: 'combobox-clear',
8438                         cn  : [
8439                             {
8440                                 tag : 'i',
8441                                 cls: 'icon-remove'
8442                             }
8443                         ]
8444                     }
8445                 ]
8446
8447             })
8448         }
8449         
8450         if(this.multiple){
8451             combobox.cls += ' select2-container-multi';
8452         }
8453         
8454         if (align ==='left' && this.fieldLabel.length) {
8455             
8456                 Roo.log("left and has label");
8457                 cfg.cn = [
8458                     
8459                     {
8460                         tag: 'label',
8461                         'for' :  id,
8462                         cls : 'control-label col-sm-' + this.labelWidth,
8463                         html : this.fieldLabel
8464                         
8465                     },
8466                     {
8467                         cls : "col-sm-" + (12 - this.labelWidth), 
8468                         cn: [
8469                             combobox
8470                         ]
8471                     }
8472                     
8473                 ];
8474         } else if ( this.fieldLabel.length) {
8475                 Roo.log(" label");
8476                  cfg.cn = [
8477                    
8478                     {
8479                         tag: 'label',
8480                         //cls : 'input-group-addon',
8481                         html : this.fieldLabel
8482                         
8483                     },
8484                     
8485                     combobox
8486                     
8487                 ];
8488
8489         } else {
8490             
8491                 Roo.log(" no label && no align");
8492                 cfg = combobox
8493                      
8494                 
8495         }
8496          
8497         var settings=this;
8498         ['xs','sm','md','lg'].map(function(size){
8499             if (settings[size]) {
8500                 cfg.cls += ' col-' + size + '-' + settings[size];
8501             }
8502         });
8503         
8504         return cfg;
8505         
8506     },
8507     
8508     
8509     
8510     // private
8511     onResize : function(w, h){
8512 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8513 //        if(typeof w == 'number'){
8514 //            var x = w - this.trigger.getWidth();
8515 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8516 //            this.trigger.setStyle('left', x+'px');
8517 //        }
8518     },
8519
8520     // private
8521     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8522
8523     // private
8524     getResizeEl : function(){
8525         return this.inputEl();
8526     },
8527
8528     // private
8529     getPositionEl : function(){
8530         return this.inputEl();
8531     },
8532
8533     // private
8534     alignErrorIcon : function(){
8535         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8536     },
8537
8538     // private
8539     initEvents : function(){
8540         
8541         this.createList();
8542         
8543         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8544         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8545         if(!this.multiple && this.showToggleBtn){
8546             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8547             if(this.hideTrigger){
8548                 this.trigger.setDisplayed(false);
8549             }
8550             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8551         }
8552         
8553         if(this.multiple){
8554             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8555         }
8556         
8557         //this.trigger.addClassOnOver('x-form-trigger-over');
8558         //this.trigger.addClassOnClick('x-form-trigger-click');
8559         
8560         //if(!this.width){
8561         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8562         //}
8563     },
8564     
8565     createList : function()
8566     {
8567         this.list = Roo.get(document.body).createChild({
8568             tag: 'ul',
8569             cls: 'typeahead typeahead-long dropdown-menu',
8570             style: 'display:none'
8571         });
8572         
8573         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8574         
8575     },
8576
8577     // private
8578     initTrigger : function(){
8579        
8580     },
8581
8582     // private
8583     onDestroy : function(){
8584         if(this.trigger){
8585             this.trigger.removeAllListeners();
8586           //  this.trigger.remove();
8587         }
8588         //if(this.wrap){
8589         //    this.wrap.remove();
8590         //}
8591         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8592     },
8593
8594     // private
8595     onFocus : function(){
8596         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8597         /*
8598         if(!this.mimicing){
8599             this.wrap.addClass('x-trigger-wrap-focus');
8600             this.mimicing = true;
8601             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8602             if(this.monitorTab){
8603                 this.el.on("keydown", this.checkTab, this);
8604             }
8605         }
8606         */
8607     },
8608
8609     // private
8610     checkTab : function(e){
8611         if(e.getKey() == e.TAB){
8612             this.triggerBlur();
8613         }
8614     },
8615
8616     // private
8617     onBlur : function(){
8618         // do nothing
8619     },
8620
8621     // private
8622     mimicBlur : function(e, t){
8623         /*
8624         if(!this.wrap.contains(t) && this.validateBlur()){
8625             this.triggerBlur();
8626         }
8627         */
8628     },
8629
8630     // private
8631     triggerBlur : function(){
8632         this.mimicing = false;
8633         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8634         if(this.monitorTab){
8635             this.el.un("keydown", this.checkTab, this);
8636         }
8637         //this.wrap.removeClass('x-trigger-wrap-focus');
8638         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8639     },
8640
8641     // private
8642     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8643     validateBlur : function(e, t){
8644         return true;
8645     },
8646
8647     // private
8648     onDisable : function(){
8649         this.inputEl().dom.disabled = true;
8650         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8651         //if(this.wrap){
8652         //    this.wrap.addClass('x-item-disabled');
8653         //}
8654     },
8655
8656     // private
8657     onEnable : function(){
8658         this.inputEl().dom.disabled = false;
8659         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8660         //if(this.wrap){
8661         //    this.el.removeClass('x-item-disabled');
8662         //}
8663     },
8664
8665     // private
8666     onShow : function(){
8667         var ae = this.getActionEl();
8668         
8669         if(ae){
8670             ae.dom.style.display = '';
8671             ae.dom.style.visibility = 'visible';
8672         }
8673     },
8674
8675     // private
8676     
8677     onHide : function(){
8678         var ae = this.getActionEl();
8679         ae.dom.style.display = 'none';
8680     },
8681
8682     /**
8683      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8684      * by an implementing function.
8685      * @method
8686      * @param {EventObject} e
8687      */
8688     onTriggerClick : Roo.emptyFn
8689 });
8690  /*
8691  * Based on:
8692  * Ext JS Library 1.1.1
8693  * Copyright(c) 2006-2007, Ext JS, LLC.
8694  *
8695  * Originally Released Under LGPL - original licence link has changed is not relivant.
8696  *
8697  * Fork - LGPL
8698  * <script type="text/javascript">
8699  */
8700
8701
8702 /**
8703  * @class Roo.data.SortTypes
8704  * @singleton
8705  * Defines the default sorting (casting?) comparison functions used when sorting data.
8706  */
8707 Roo.data.SortTypes = {
8708     /**
8709      * Default sort that does nothing
8710      * @param {Mixed} s The value being converted
8711      * @return {Mixed} The comparison value
8712      */
8713     none : function(s){
8714         return s;
8715     },
8716     
8717     /**
8718      * The regular expression used to strip tags
8719      * @type {RegExp}
8720      * @property
8721      */
8722     stripTagsRE : /<\/?[^>]+>/gi,
8723     
8724     /**
8725      * Strips all HTML tags to sort on text only
8726      * @param {Mixed} s The value being converted
8727      * @return {String} The comparison value
8728      */
8729     asText : function(s){
8730         return String(s).replace(this.stripTagsRE, "");
8731     },
8732     
8733     /**
8734      * Strips all HTML tags to sort on text only - Case insensitive
8735      * @param {Mixed} s The value being converted
8736      * @return {String} The comparison value
8737      */
8738     asUCText : function(s){
8739         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8740     },
8741     
8742     /**
8743      * Case insensitive string
8744      * @param {Mixed} s The value being converted
8745      * @return {String} The comparison value
8746      */
8747     asUCString : function(s) {
8748         return String(s).toUpperCase();
8749     },
8750     
8751     /**
8752      * Date sorting
8753      * @param {Mixed} s The value being converted
8754      * @return {Number} The comparison value
8755      */
8756     asDate : function(s) {
8757         if(!s){
8758             return 0;
8759         }
8760         if(s instanceof Date){
8761             return s.getTime();
8762         }
8763         return Date.parse(String(s));
8764     },
8765     
8766     /**
8767      * Float sorting
8768      * @param {Mixed} s The value being converted
8769      * @return {Float} The comparison value
8770      */
8771     asFloat : function(s) {
8772         var val = parseFloat(String(s).replace(/,/g, ""));
8773         if(isNaN(val)) val = 0;
8774         return val;
8775     },
8776     
8777     /**
8778      * Integer sorting
8779      * @param {Mixed} s The value being converted
8780      * @return {Number} The comparison value
8781      */
8782     asInt : function(s) {
8783         var val = parseInt(String(s).replace(/,/g, ""));
8784         if(isNaN(val)) val = 0;
8785         return val;
8786     }
8787 };/*
8788  * Based on:
8789  * Ext JS Library 1.1.1
8790  * Copyright(c) 2006-2007, Ext JS, LLC.
8791  *
8792  * Originally Released Under LGPL - original licence link has changed is not relivant.
8793  *
8794  * Fork - LGPL
8795  * <script type="text/javascript">
8796  */
8797
8798 /**
8799 * @class Roo.data.Record
8800  * Instances of this class encapsulate both record <em>definition</em> information, and record
8801  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8802  * to access Records cached in an {@link Roo.data.Store} object.<br>
8803  * <p>
8804  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8805  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8806  * objects.<br>
8807  * <p>
8808  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8809  * @constructor
8810  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8811  * {@link #create}. The parameters are the same.
8812  * @param {Array} data An associative Array of data values keyed by the field name.
8813  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8814  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8815  * not specified an integer id is generated.
8816  */
8817 Roo.data.Record = function(data, id){
8818     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8819     this.data = data;
8820 };
8821
8822 /**
8823  * Generate a constructor for a specific record layout.
8824  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8825  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8826  * Each field definition object may contain the following properties: <ul>
8827  * <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,
8828  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8829  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8830  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8831  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8832  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8833  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8834  * this may be omitted.</p></li>
8835  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8836  * <ul><li>auto (Default, implies no conversion)</li>
8837  * <li>string</li>
8838  * <li>int</li>
8839  * <li>float</li>
8840  * <li>boolean</li>
8841  * <li>date</li></ul></p></li>
8842  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8843  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8844  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8845  * by the Reader into an object that will be stored in the Record. It is passed the
8846  * following parameters:<ul>
8847  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8848  * </ul></p></li>
8849  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8850  * </ul>
8851  * <br>usage:<br><pre><code>
8852 var TopicRecord = Roo.data.Record.create(
8853     {name: 'title', mapping: 'topic_title'},
8854     {name: 'author', mapping: 'username'},
8855     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8856     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8857     {name: 'lastPoster', mapping: 'user2'},
8858     {name: 'excerpt', mapping: 'post_text'}
8859 );
8860
8861 var myNewRecord = new TopicRecord({
8862     title: 'Do my job please',
8863     author: 'noobie',
8864     totalPosts: 1,
8865     lastPost: new Date(),
8866     lastPoster: 'Animal',
8867     excerpt: 'No way dude!'
8868 });
8869 myStore.add(myNewRecord);
8870 </code></pre>
8871  * @method create
8872  * @static
8873  */
8874 Roo.data.Record.create = function(o){
8875     var f = function(){
8876         f.superclass.constructor.apply(this, arguments);
8877     };
8878     Roo.extend(f, Roo.data.Record);
8879     var p = f.prototype;
8880     p.fields = new Roo.util.MixedCollection(false, function(field){
8881         return field.name;
8882     });
8883     for(var i = 0, len = o.length; i < len; i++){
8884         p.fields.add(new Roo.data.Field(o[i]));
8885     }
8886     f.getField = function(name){
8887         return p.fields.get(name);  
8888     };
8889     return f;
8890 };
8891
8892 Roo.data.Record.AUTO_ID = 1000;
8893 Roo.data.Record.EDIT = 'edit';
8894 Roo.data.Record.REJECT = 'reject';
8895 Roo.data.Record.COMMIT = 'commit';
8896
8897 Roo.data.Record.prototype = {
8898     /**
8899      * Readonly flag - true if this record has been modified.
8900      * @type Boolean
8901      */
8902     dirty : false,
8903     editing : false,
8904     error: null,
8905     modified: null,
8906
8907     // private
8908     join : function(store){
8909         this.store = store;
8910     },
8911
8912     /**
8913      * Set the named field to the specified value.
8914      * @param {String} name The name of the field to set.
8915      * @param {Object} value The value to set the field to.
8916      */
8917     set : function(name, value){
8918         if(this.data[name] == value){
8919             return;
8920         }
8921         this.dirty = true;
8922         if(!this.modified){
8923             this.modified = {};
8924         }
8925         if(typeof this.modified[name] == 'undefined'){
8926             this.modified[name] = this.data[name];
8927         }
8928         this.data[name] = value;
8929         if(!this.editing && this.store){
8930             this.store.afterEdit(this);
8931         }       
8932     },
8933
8934     /**
8935      * Get the value of the named field.
8936      * @param {String} name The name of the field to get the value of.
8937      * @return {Object} The value of the field.
8938      */
8939     get : function(name){
8940         return this.data[name]; 
8941     },
8942
8943     // private
8944     beginEdit : function(){
8945         this.editing = true;
8946         this.modified = {}; 
8947     },
8948
8949     // private
8950     cancelEdit : function(){
8951         this.editing = false;
8952         delete this.modified;
8953     },
8954
8955     // private
8956     endEdit : function(){
8957         this.editing = false;
8958         if(this.dirty && this.store){
8959             this.store.afterEdit(this);
8960         }
8961     },
8962
8963     /**
8964      * Usually called by the {@link Roo.data.Store} which owns the Record.
8965      * Rejects all changes made to the Record since either creation, or the last commit operation.
8966      * Modified fields are reverted to their original values.
8967      * <p>
8968      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8969      * of reject operations.
8970      */
8971     reject : function(){
8972         var m = this.modified;
8973         for(var n in m){
8974             if(typeof m[n] != "function"){
8975                 this.data[n] = m[n];
8976             }
8977         }
8978         this.dirty = false;
8979         delete this.modified;
8980         this.editing = false;
8981         if(this.store){
8982             this.store.afterReject(this);
8983         }
8984     },
8985
8986     /**
8987      * Usually called by the {@link Roo.data.Store} which owns the Record.
8988      * Commits all changes made to the Record since either creation, or the last commit operation.
8989      * <p>
8990      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8991      * of commit operations.
8992      */
8993     commit : function(){
8994         this.dirty = false;
8995         delete this.modified;
8996         this.editing = false;
8997         if(this.store){
8998             this.store.afterCommit(this);
8999         }
9000     },
9001
9002     // private
9003     hasError : function(){
9004         return this.error != null;
9005     },
9006
9007     // private
9008     clearError : function(){
9009         this.error = null;
9010     },
9011
9012     /**
9013      * Creates a copy of this record.
9014      * @param {String} id (optional) A new record id if you don't want to use this record's id
9015      * @return {Record}
9016      */
9017     copy : function(newId) {
9018         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9019     }
9020 };/*
9021  * Based on:
9022  * Ext JS Library 1.1.1
9023  * Copyright(c) 2006-2007, Ext JS, LLC.
9024  *
9025  * Originally Released Under LGPL - original licence link has changed is not relivant.
9026  *
9027  * Fork - LGPL
9028  * <script type="text/javascript">
9029  */
9030
9031
9032
9033 /**
9034  * @class Roo.data.Store
9035  * @extends Roo.util.Observable
9036  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9037  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9038  * <p>
9039  * 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
9040  * has no knowledge of the format of the data returned by the Proxy.<br>
9041  * <p>
9042  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9043  * instances from the data object. These records are cached and made available through accessor functions.
9044  * @constructor
9045  * Creates a new Store.
9046  * @param {Object} config A config object containing the objects needed for the Store to access data,
9047  * and read the data into Records.
9048  */
9049 Roo.data.Store = function(config){
9050     this.data = new Roo.util.MixedCollection(false);
9051     this.data.getKey = function(o){
9052         return o.id;
9053     };
9054     this.baseParams = {};
9055     // private
9056     this.paramNames = {
9057         "start" : "start",
9058         "limit" : "limit",
9059         "sort" : "sort",
9060         "dir" : "dir",
9061         "multisort" : "_multisort"
9062     };
9063
9064     if(config && config.data){
9065         this.inlineData = config.data;
9066         delete config.data;
9067     }
9068
9069     Roo.apply(this, config);
9070     
9071     if(this.reader){ // reader passed
9072         this.reader = Roo.factory(this.reader, Roo.data);
9073         this.reader.xmodule = this.xmodule || false;
9074         if(!this.recordType){
9075             this.recordType = this.reader.recordType;
9076         }
9077         if(this.reader.onMetaChange){
9078             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9079         }
9080     }
9081
9082     if(this.recordType){
9083         this.fields = this.recordType.prototype.fields;
9084     }
9085     this.modified = [];
9086
9087     this.addEvents({
9088         /**
9089          * @event datachanged
9090          * Fires when the data cache has changed, and a widget which is using this Store
9091          * as a Record cache should refresh its view.
9092          * @param {Store} this
9093          */
9094         datachanged : true,
9095         /**
9096          * @event metachange
9097          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9098          * @param {Store} this
9099          * @param {Object} meta The JSON metadata
9100          */
9101         metachange : true,
9102         /**
9103          * @event add
9104          * Fires when Records have been added to the Store
9105          * @param {Store} this
9106          * @param {Roo.data.Record[]} records The array of Records added
9107          * @param {Number} index The index at which the record(s) were added
9108          */
9109         add : true,
9110         /**
9111          * @event remove
9112          * Fires when a Record has been removed from the Store
9113          * @param {Store} this
9114          * @param {Roo.data.Record} record The Record that was removed
9115          * @param {Number} index The index at which the record was removed
9116          */
9117         remove : true,
9118         /**
9119          * @event update
9120          * Fires when a Record has been updated
9121          * @param {Store} this
9122          * @param {Roo.data.Record} record The Record that was updated
9123          * @param {String} operation The update operation being performed.  Value may be one of:
9124          * <pre><code>
9125  Roo.data.Record.EDIT
9126  Roo.data.Record.REJECT
9127  Roo.data.Record.COMMIT
9128          * </code></pre>
9129          */
9130         update : true,
9131         /**
9132          * @event clear
9133          * Fires when the data cache has been cleared.
9134          * @param {Store} this
9135          */
9136         clear : true,
9137         /**
9138          * @event beforeload
9139          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9140          * the load action will be canceled.
9141          * @param {Store} this
9142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9143          */
9144         beforeload : true,
9145         /**
9146          * @event beforeloadadd
9147          * Fires after a new set of Records has been loaded.
9148          * @param {Store} this
9149          * @param {Roo.data.Record[]} records The Records that were loaded
9150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9151          */
9152         beforeloadadd : true,
9153         /**
9154          * @event load
9155          * Fires after a new set of Records has been loaded, before they are added to the store.
9156          * @param {Store} this
9157          * @param {Roo.data.Record[]} records The Records that were loaded
9158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9159          * @params {Object} return from reader
9160          */
9161         load : true,
9162         /**
9163          * @event loadexception
9164          * Fires if an exception occurs in the Proxy during loading.
9165          * Called with the signature of the Proxy's "loadexception" event.
9166          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9167          * 
9168          * @param {Proxy} 
9169          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9170          * @param {Object} load options 
9171          * @param {Object} jsonData from your request (normally this contains the Exception)
9172          */
9173         loadexception : true
9174     });
9175     
9176     if(this.proxy){
9177         this.proxy = Roo.factory(this.proxy, Roo.data);
9178         this.proxy.xmodule = this.xmodule || false;
9179         this.relayEvents(this.proxy,  ["loadexception"]);
9180     }
9181     this.sortToggle = {};
9182     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9183
9184     Roo.data.Store.superclass.constructor.call(this);
9185
9186     if(this.inlineData){
9187         this.loadData(this.inlineData);
9188         delete this.inlineData;
9189     }
9190 };
9191
9192 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9193      /**
9194     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9195     * without a remote query - used by combo/forms at present.
9196     */
9197     
9198     /**
9199     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9200     */
9201     /**
9202     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9203     */
9204     /**
9205     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9206     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9207     */
9208     /**
9209     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9210     * on any HTTP request
9211     */
9212     /**
9213     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9214     */
9215     /**
9216     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9217     */
9218     multiSort: false,
9219     /**
9220     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9221     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9222     */
9223     remoteSort : false,
9224
9225     /**
9226     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9227      * loaded or when a record is removed. (defaults to false).
9228     */
9229     pruneModifiedRecords : false,
9230
9231     // private
9232     lastOptions : null,
9233
9234     /**
9235      * Add Records to the Store and fires the add event.
9236      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9237      */
9238     add : function(records){
9239         records = [].concat(records);
9240         for(var i = 0, len = records.length; i < len; i++){
9241             records[i].join(this);
9242         }
9243         var index = this.data.length;
9244         this.data.addAll(records);
9245         this.fireEvent("add", this, records, index);
9246     },
9247
9248     /**
9249      * Remove a Record from the Store and fires the remove event.
9250      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9251      */
9252     remove : function(record){
9253         var index = this.data.indexOf(record);
9254         this.data.removeAt(index);
9255         if(this.pruneModifiedRecords){
9256             this.modified.remove(record);
9257         }
9258         this.fireEvent("remove", this, record, index);
9259     },
9260
9261     /**
9262      * Remove all Records from the Store and fires the clear event.
9263      */
9264     removeAll : function(){
9265         this.data.clear();
9266         if(this.pruneModifiedRecords){
9267             this.modified = [];
9268         }
9269         this.fireEvent("clear", this);
9270     },
9271
9272     /**
9273      * Inserts Records to the Store at the given index and fires the add event.
9274      * @param {Number} index The start index at which to insert the passed Records.
9275      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9276      */
9277     insert : function(index, records){
9278         records = [].concat(records);
9279         for(var i = 0, len = records.length; i < len; i++){
9280             this.data.insert(index, records[i]);
9281             records[i].join(this);
9282         }
9283         this.fireEvent("add", this, records, index);
9284     },
9285
9286     /**
9287      * Get the index within the cache of the passed Record.
9288      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9289      * @return {Number} The index of the passed Record. Returns -1 if not found.
9290      */
9291     indexOf : function(record){
9292         return this.data.indexOf(record);
9293     },
9294
9295     /**
9296      * Get the index within the cache of the Record with the passed id.
9297      * @param {String} id The id of the Record to find.
9298      * @return {Number} The index of the Record. Returns -1 if not found.
9299      */
9300     indexOfId : function(id){
9301         return this.data.indexOfKey(id);
9302     },
9303
9304     /**
9305      * Get the Record with the specified id.
9306      * @param {String} id The id of the Record to find.
9307      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9308      */
9309     getById : function(id){
9310         return this.data.key(id);
9311     },
9312
9313     /**
9314      * Get the Record at the specified index.
9315      * @param {Number} index The index of the Record to find.
9316      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9317      */
9318     getAt : function(index){
9319         return this.data.itemAt(index);
9320     },
9321
9322     /**
9323      * Returns a range of Records between specified indices.
9324      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9325      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9326      * @return {Roo.data.Record[]} An array of Records
9327      */
9328     getRange : function(start, end){
9329         return this.data.getRange(start, end);
9330     },
9331
9332     // private
9333     storeOptions : function(o){
9334         o = Roo.apply({}, o);
9335         delete o.callback;
9336         delete o.scope;
9337         this.lastOptions = o;
9338     },
9339
9340     /**
9341      * Loads the Record cache from the configured Proxy using the configured Reader.
9342      * <p>
9343      * If using remote paging, then the first load call must specify the <em>start</em>
9344      * and <em>limit</em> properties in the options.params property to establish the initial
9345      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9346      * <p>
9347      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9348      * and this call will return before the new data has been loaded. Perform any post-processing
9349      * in a callback function, or in a "load" event handler.</strong>
9350      * <p>
9351      * @param {Object} options An object containing properties which control loading options:<ul>
9352      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9353      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9354      * passed the following arguments:<ul>
9355      * <li>r : Roo.data.Record[]</li>
9356      * <li>options: Options object from the load call</li>
9357      * <li>success: Boolean success indicator</li></ul></li>
9358      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9359      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9360      * </ul>
9361      */
9362     load : function(options){
9363         options = options || {};
9364         if(this.fireEvent("beforeload", this, options) !== false){
9365             this.storeOptions(options);
9366             var p = Roo.apply(options.params || {}, this.baseParams);
9367             // if meta was not loaded from remote source.. try requesting it.
9368             if (!this.reader.metaFromRemote) {
9369                 p._requestMeta = 1;
9370             }
9371             if(this.sortInfo && this.remoteSort){
9372                 var pn = this.paramNames;
9373                 p[pn["sort"]] = this.sortInfo.field;
9374                 p[pn["dir"]] = this.sortInfo.direction;
9375             }
9376             if (this.multiSort) {
9377                 var pn = this.paramNames;
9378                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9379             }
9380             
9381             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9382         }
9383     },
9384
9385     /**
9386      * Reloads the Record cache from the configured Proxy using the configured Reader and
9387      * the options from the last load operation performed.
9388      * @param {Object} options (optional) An object containing properties which may override the options
9389      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9390      * the most recently used options are reused).
9391      */
9392     reload : function(options){
9393         this.load(Roo.applyIf(options||{}, this.lastOptions));
9394     },
9395
9396     // private
9397     // Called as a callback by the Reader during a load operation.
9398     loadRecords : function(o, options, success){
9399         if(!o || success === false){
9400             if(success !== false){
9401                 this.fireEvent("load", this, [], options, o);
9402             }
9403             if(options.callback){
9404                 options.callback.call(options.scope || this, [], options, false);
9405             }
9406             return;
9407         }
9408         // if data returned failure - throw an exception.
9409         if (o.success === false) {
9410             // show a message if no listener is registered.
9411             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9412                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9413             }
9414             // loadmask wil be hooked into this..
9415             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9416             return;
9417         }
9418         var r = o.records, t = o.totalRecords || r.length;
9419         
9420         this.fireEvent("beforeloadadd", this, r, options, o);
9421         
9422         if(!options || options.add !== true){
9423             if(this.pruneModifiedRecords){
9424                 this.modified = [];
9425             }
9426             for(var i = 0, len = r.length; i < len; i++){
9427                 r[i].join(this);
9428             }
9429             if(this.snapshot){
9430                 this.data = this.snapshot;
9431                 delete this.snapshot;
9432             }
9433             this.data.clear();
9434             this.data.addAll(r);
9435             this.totalLength = t;
9436             this.applySort();
9437             this.fireEvent("datachanged", this);
9438         }else{
9439             this.totalLength = Math.max(t, this.data.length+r.length);
9440             this.add(r);
9441         }
9442         this.fireEvent("load", this, r, options, o);
9443         if(options.callback){
9444             options.callback.call(options.scope || this, r, options, true);
9445         }
9446     },
9447
9448
9449     /**
9450      * Loads data from a passed data block. A Reader which understands the format of the data
9451      * must have been configured in the constructor.
9452      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9453      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9454      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9455      */
9456     loadData : function(o, append){
9457         var r = this.reader.readRecords(o);
9458         this.loadRecords(r, {add: append}, true);
9459     },
9460
9461     /**
9462      * Gets the number of cached records.
9463      * <p>
9464      * <em>If using paging, this may not be the total size of the dataset. If the data object
9465      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9466      * the data set size</em>
9467      */
9468     getCount : function(){
9469         return this.data.length || 0;
9470     },
9471
9472     /**
9473      * Gets the total number of records in the dataset as returned by the server.
9474      * <p>
9475      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9476      * the dataset size</em>
9477      */
9478     getTotalCount : function(){
9479         return this.totalLength || 0;
9480     },
9481
9482     /**
9483      * Returns the sort state of the Store as an object with two properties:
9484      * <pre><code>
9485  field {String} The name of the field by which the Records are sorted
9486  direction {String} The sort order, "ASC" or "DESC"
9487      * </code></pre>
9488      */
9489     getSortState : function(){
9490         return this.sortInfo;
9491     },
9492
9493     // private
9494     applySort : function(){
9495         if(this.sortInfo && !this.remoteSort){
9496             var s = this.sortInfo, f = s.field;
9497             var st = this.fields.get(f).sortType;
9498             var fn = function(r1, r2){
9499                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9500                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9501             };
9502             this.data.sort(s.direction, fn);
9503             if(this.snapshot && this.snapshot != this.data){
9504                 this.snapshot.sort(s.direction, fn);
9505             }
9506         }
9507     },
9508
9509     /**
9510      * Sets the default sort column and order to be used by the next load operation.
9511      * @param {String} fieldName The name of the field to sort by.
9512      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9513      */
9514     setDefaultSort : function(field, dir){
9515         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9516     },
9517
9518     /**
9519      * Sort the Records.
9520      * If remote sorting is used, the sort is performed on the server, and the cache is
9521      * reloaded. If local sorting is used, the cache is sorted internally.
9522      * @param {String} fieldName The name of the field to sort by.
9523      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9524      */
9525     sort : function(fieldName, dir){
9526         var f = this.fields.get(fieldName);
9527         if(!dir){
9528             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9529             
9530             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9531                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9532             }else{
9533                 dir = f.sortDir;
9534             }
9535         }
9536         this.sortToggle[f.name] = dir;
9537         this.sortInfo = {field: f.name, direction: dir};
9538         if(!this.remoteSort){
9539             this.applySort();
9540             this.fireEvent("datachanged", this);
9541         }else{
9542             this.load(this.lastOptions);
9543         }
9544     },
9545
9546     /**
9547      * Calls the specified function for each of the Records in the cache.
9548      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9549      * Returning <em>false</em> aborts and exits the iteration.
9550      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9551      */
9552     each : function(fn, scope){
9553         this.data.each(fn, scope);
9554     },
9555
9556     /**
9557      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9558      * (e.g., during paging).
9559      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9560      */
9561     getModifiedRecords : function(){
9562         return this.modified;
9563     },
9564
9565     // private
9566     createFilterFn : function(property, value, anyMatch){
9567         if(!value.exec){ // not a regex
9568             value = String(value);
9569             if(value.length == 0){
9570                 return false;
9571             }
9572             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9573         }
9574         return function(r){
9575             return value.test(r.data[property]);
9576         };
9577     },
9578
9579     /**
9580      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9581      * @param {String} property A field on your records
9582      * @param {Number} start The record index to start at (defaults to 0)
9583      * @param {Number} end The last record index to include (defaults to length - 1)
9584      * @return {Number} The sum
9585      */
9586     sum : function(property, start, end){
9587         var rs = this.data.items, v = 0;
9588         start = start || 0;
9589         end = (end || end === 0) ? end : rs.length-1;
9590
9591         for(var i = start; i <= end; i++){
9592             v += (rs[i].data[property] || 0);
9593         }
9594         return v;
9595     },
9596
9597     /**
9598      * Filter the records by a specified property.
9599      * @param {String} field A field on your records
9600      * @param {String/RegExp} value Either a string that the field
9601      * should start with or a RegExp to test against the field
9602      * @param {Boolean} anyMatch True to match any part not just the beginning
9603      */
9604     filter : function(property, value, anyMatch){
9605         var fn = this.createFilterFn(property, value, anyMatch);
9606         return fn ? this.filterBy(fn) : this.clearFilter();
9607     },
9608
9609     /**
9610      * Filter by a function. The specified function will be called with each
9611      * record in this data source. If the function returns true the record is included,
9612      * otherwise it is filtered.
9613      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9614      * @param {Object} scope (optional) The scope of the function (defaults to this)
9615      */
9616     filterBy : function(fn, scope){
9617         this.snapshot = this.snapshot || this.data;
9618         this.data = this.queryBy(fn, scope||this);
9619         this.fireEvent("datachanged", this);
9620     },
9621
9622     /**
9623      * Query the records by a specified property.
9624      * @param {String} field A field on your records
9625      * @param {String/RegExp} value Either a string that the field
9626      * should start with or a RegExp to test against the field
9627      * @param {Boolean} anyMatch True to match any part not just the beginning
9628      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9629      */
9630     query : function(property, value, anyMatch){
9631         var fn = this.createFilterFn(property, value, anyMatch);
9632         return fn ? this.queryBy(fn) : this.data.clone();
9633     },
9634
9635     /**
9636      * Query by a function. The specified function will be called with each
9637      * record in this data source. If the function returns true the record is included
9638      * in the results.
9639      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9640      * @param {Object} scope (optional) The scope of the function (defaults to this)
9641       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9642      **/
9643     queryBy : function(fn, scope){
9644         var data = this.snapshot || this.data;
9645         return data.filterBy(fn, scope||this);
9646     },
9647
9648     /**
9649      * Collects unique values for a particular dataIndex from this store.
9650      * @param {String} dataIndex The property to collect
9651      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9652      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9653      * @return {Array} An array of the unique values
9654      **/
9655     collect : function(dataIndex, allowNull, bypassFilter){
9656         var d = (bypassFilter === true && this.snapshot) ?
9657                 this.snapshot.items : this.data.items;
9658         var v, sv, r = [], l = {};
9659         for(var i = 0, len = d.length; i < len; i++){
9660             v = d[i].data[dataIndex];
9661             sv = String(v);
9662             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9663                 l[sv] = true;
9664                 r[r.length] = v;
9665             }
9666         }
9667         return r;
9668     },
9669
9670     /**
9671      * Revert to a view of the Record cache with no filtering applied.
9672      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9673      */
9674     clearFilter : function(suppressEvent){
9675         if(this.snapshot && this.snapshot != this.data){
9676             this.data = this.snapshot;
9677             delete this.snapshot;
9678             if(suppressEvent !== true){
9679                 this.fireEvent("datachanged", this);
9680             }
9681         }
9682     },
9683
9684     // private
9685     afterEdit : function(record){
9686         if(this.modified.indexOf(record) == -1){
9687             this.modified.push(record);
9688         }
9689         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9690     },
9691     
9692     // private
9693     afterReject : function(record){
9694         this.modified.remove(record);
9695         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9696     },
9697
9698     // private
9699     afterCommit : function(record){
9700         this.modified.remove(record);
9701         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9702     },
9703
9704     /**
9705      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9706      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9707      */
9708     commitChanges : function(){
9709         var m = this.modified.slice(0);
9710         this.modified = [];
9711         for(var i = 0, len = m.length; i < len; i++){
9712             m[i].commit();
9713         }
9714     },
9715
9716     /**
9717      * Cancel outstanding changes on all changed records.
9718      */
9719     rejectChanges : function(){
9720         var m = this.modified.slice(0);
9721         this.modified = [];
9722         for(var i = 0, len = m.length; i < len; i++){
9723             m[i].reject();
9724         }
9725     },
9726
9727     onMetaChange : function(meta, rtype, o){
9728         this.recordType = rtype;
9729         this.fields = rtype.prototype.fields;
9730         delete this.snapshot;
9731         this.sortInfo = meta.sortInfo || this.sortInfo;
9732         this.modified = [];
9733         this.fireEvent('metachange', this, this.reader.meta);
9734     },
9735     
9736     moveIndex : function(data, type)
9737     {
9738         var index = this.indexOf(data);
9739         
9740         var newIndex = index + type;
9741         
9742         this.remove(data);
9743         
9744         this.insert(newIndex, data);
9745         
9746     }
9747 });/*
9748  * Based on:
9749  * Ext JS Library 1.1.1
9750  * Copyright(c) 2006-2007, Ext JS, LLC.
9751  *
9752  * Originally Released Under LGPL - original licence link has changed is not relivant.
9753  *
9754  * Fork - LGPL
9755  * <script type="text/javascript">
9756  */
9757
9758 /**
9759  * @class Roo.data.SimpleStore
9760  * @extends Roo.data.Store
9761  * Small helper class to make creating Stores from Array data easier.
9762  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9763  * @cfg {Array} fields An array of field definition objects, or field name strings.
9764  * @cfg {Array} data The multi-dimensional array of data
9765  * @constructor
9766  * @param {Object} config
9767  */
9768 Roo.data.SimpleStore = function(config){
9769     Roo.data.SimpleStore.superclass.constructor.call(this, {
9770         isLocal : true,
9771         reader: new Roo.data.ArrayReader({
9772                 id: config.id
9773             },
9774             Roo.data.Record.create(config.fields)
9775         ),
9776         proxy : new Roo.data.MemoryProxy(config.data)
9777     });
9778     this.load();
9779 };
9780 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9781  * Based on:
9782  * Ext JS Library 1.1.1
9783  * Copyright(c) 2006-2007, Ext JS, LLC.
9784  *
9785  * Originally Released Under LGPL - original licence link has changed is not relivant.
9786  *
9787  * Fork - LGPL
9788  * <script type="text/javascript">
9789  */
9790
9791 /**
9792 /**
9793  * @extends Roo.data.Store
9794  * @class Roo.data.JsonStore
9795  * Small helper class to make creating Stores for JSON data easier. <br/>
9796 <pre><code>
9797 var store = new Roo.data.JsonStore({
9798     url: 'get-images.php',
9799     root: 'images',
9800     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9801 });
9802 </code></pre>
9803  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9804  * JsonReader and HttpProxy (unless inline data is provided).</b>
9805  * @cfg {Array} fields An array of field definition objects, or field name strings.
9806  * @constructor
9807  * @param {Object} config
9808  */
9809 Roo.data.JsonStore = function(c){
9810     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9811         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9812         reader: new Roo.data.JsonReader(c, c.fields)
9813     }));
9814 };
9815 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9816  * Based on:
9817  * Ext JS Library 1.1.1
9818  * Copyright(c) 2006-2007, Ext JS, LLC.
9819  *
9820  * Originally Released Under LGPL - original licence link has changed is not relivant.
9821  *
9822  * Fork - LGPL
9823  * <script type="text/javascript">
9824  */
9825
9826  
9827 Roo.data.Field = function(config){
9828     if(typeof config == "string"){
9829         config = {name: config};
9830     }
9831     Roo.apply(this, config);
9832     
9833     if(!this.type){
9834         this.type = "auto";
9835     }
9836     
9837     var st = Roo.data.SortTypes;
9838     // named sortTypes are supported, here we look them up
9839     if(typeof this.sortType == "string"){
9840         this.sortType = st[this.sortType];
9841     }
9842     
9843     // set default sortType for strings and dates
9844     if(!this.sortType){
9845         switch(this.type){
9846             case "string":
9847                 this.sortType = st.asUCString;
9848                 break;
9849             case "date":
9850                 this.sortType = st.asDate;
9851                 break;
9852             default:
9853                 this.sortType = st.none;
9854         }
9855     }
9856
9857     // define once
9858     var stripRe = /[\$,%]/g;
9859
9860     // prebuilt conversion function for this field, instead of
9861     // switching every time we're reading a value
9862     if(!this.convert){
9863         var cv, dateFormat = this.dateFormat;
9864         switch(this.type){
9865             case "":
9866             case "auto":
9867             case undefined:
9868                 cv = function(v){ return v; };
9869                 break;
9870             case "string":
9871                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9872                 break;
9873             case "int":
9874                 cv = function(v){
9875                     return v !== undefined && v !== null && v !== '' ?
9876                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9877                     };
9878                 break;
9879             case "float":
9880                 cv = function(v){
9881                     return v !== undefined && v !== null && v !== '' ?
9882                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9883                     };
9884                 break;
9885             case "bool":
9886             case "boolean":
9887                 cv = function(v){ return v === true || v === "true" || v == 1; };
9888                 break;
9889             case "date":
9890                 cv = function(v){
9891                     if(!v){
9892                         return '';
9893                     }
9894                     if(v instanceof Date){
9895                         return v;
9896                     }
9897                     if(dateFormat){
9898                         if(dateFormat == "timestamp"){
9899                             return new Date(v*1000);
9900                         }
9901                         return Date.parseDate(v, dateFormat);
9902                     }
9903                     var parsed = Date.parse(v);
9904                     return parsed ? new Date(parsed) : null;
9905                 };
9906              break;
9907             
9908         }
9909         this.convert = cv;
9910     }
9911 };
9912
9913 Roo.data.Field.prototype = {
9914     dateFormat: null,
9915     defaultValue: "",
9916     mapping: null,
9917     sortType : null,
9918     sortDir : "ASC"
9919 };/*
9920  * Based on:
9921  * Ext JS Library 1.1.1
9922  * Copyright(c) 2006-2007, Ext JS, LLC.
9923  *
9924  * Originally Released Under LGPL - original licence link has changed is not relivant.
9925  *
9926  * Fork - LGPL
9927  * <script type="text/javascript">
9928  */
9929  
9930 // Base class for reading structured data from a data source.  This class is intended to be
9931 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9932
9933 /**
9934  * @class Roo.data.DataReader
9935  * Base class for reading structured data from a data source.  This class is intended to be
9936  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9937  */
9938
9939 Roo.data.DataReader = function(meta, recordType){
9940     
9941     this.meta = meta;
9942     
9943     this.recordType = recordType instanceof Array ? 
9944         Roo.data.Record.create(recordType) : recordType;
9945 };
9946
9947 Roo.data.DataReader.prototype = {
9948      /**
9949      * Create an empty record
9950      * @param {Object} data (optional) - overlay some values
9951      * @return {Roo.data.Record} record created.
9952      */
9953     newRow :  function(d) {
9954         var da =  {};
9955         this.recordType.prototype.fields.each(function(c) {
9956             switch( c.type) {
9957                 case 'int' : da[c.name] = 0; break;
9958                 case 'date' : da[c.name] = new Date(); break;
9959                 case 'float' : da[c.name] = 0.0; break;
9960                 case 'boolean' : da[c.name] = false; break;
9961                 default : da[c.name] = ""; break;
9962             }
9963             
9964         });
9965         return new this.recordType(Roo.apply(da, d));
9966     }
9967     
9968 };/*
9969  * Based on:
9970  * Ext JS Library 1.1.1
9971  * Copyright(c) 2006-2007, Ext JS, LLC.
9972  *
9973  * Originally Released Under LGPL - original licence link has changed is not relivant.
9974  *
9975  * Fork - LGPL
9976  * <script type="text/javascript">
9977  */
9978
9979 /**
9980  * @class Roo.data.DataProxy
9981  * @extends Roo.data.Observable
9982  * This class is an abstract base class for implementations which provide retrieval of
9983  * unformatted data objects.<br>
9984  * <p>
9985  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9986  * (of the appropriate type which knows how to parse the data object) to provide a block of
9987  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9988  * <p>
9989  * Custom implementations must implement the load method as described in
9990  * {@link Roo.data.HttpProxy#load}.
9991  */
9992 Roo.data.DataProxy = function(){
9993     this.addEvents({
9994         /**
9995          * @event beforeload
9996          * Fires before a network request is made to retrieve a data object.
9997          * @param {Object} This DataProxy object.
9998          * @param {Object} params The params parameter to the load function.
9999          */
10000         beforeload : true,
10001         /**
10002          * @event load
10003          * Fires before the load method's callback is called.
10004          * @param {Object} This DataProxy object.
10005          * @param {Object} o The data object.
10006          * @param {Object} arg The callback argument object passed to the load function.
10007          */
10008         load : true,
10009         /**
10010          * @event loadexception
10011          * Fires if an Exception occurs during data retrieval.
10012          * @param {Object} This DataProxy object.
10013          * @param {Object} o The data object.
10014          * @param {Object} arg The callback argument object passed to the load function.
10015          * @param {Object} e The Exception.
10016          */
10017         loadexception : true
10018     });
10019     Roo.data.DataProxy.superclass.constructor.call(this);
10020 };
10021
10022 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10023
10024     /**
10025      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10026      */
10027 /*
10028  * Based on:
10029  * Ext JS Library 1.1.1
10030  * Copyright(c) 2006-2007, Ext JS, LLC.
10031  *
10032  * Originally Released Under LGPL - original licence link has changed is not relivant.
10033  *
10034  * Fork - LGPL
10035  * <script type="text/javascript">
10036  */
10037 /**
10038  * @class Roo.data.MemoryProxy
10039  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10040  * to the Reader when its load method is called.
10041  * @constructor
10042  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10043  */
10044 Roo.data.MemoryProxy = function(data){
10045     if (data.data) {
10046         data = data.data;
10047     }
10048     Roo.data.MemoryProxy.superclass.constructor.call(this);
10049     this.data = data;
10050 };
10051
10052 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10053     /**
10054      * Load data from the requested source (in this case an in-memory
10055      * data object passed to the constructor), read the data object into
10056      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10057      * process that block using the passed callback.
10058      * @param {Object} params This parameter is not used by the MemoryProxy class.
10059      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10060      * object into a block of Roo.data.Records.
10061      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10062      * The function must be passed <ul>
10063      * <li>The Record block object</li>
10064      * <li>The "arg" argument from the load function</li>
10065      * <li>A boolean success indicator</li>
10066      * </ul>
10067      * @param {Object} scope The scope in which to call the callback
10068      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10069      */
10070     load : function(params, reader, callback, scope, arg){
10071         params = params || {};
10072         var result;
10073         try {
10074             result = reader.readRecords(this.data);
10075         }catch(e){
10076             this.fireEvent("loadexception", this, arg, null, e);
10077             callback.call(scope, null, arg, false);
10078             return;
10079         }
10080         callback.call(scope, result, arg, true);
10081     },
10082     
10083     // private
10084     update : function(params, records){
10085         
10086     }
10087 });/*
10088  * Based on:
10089  * Ext JS Library 1.1.1
10090  * Copyright(c) 2006-2007, Ext JS, LLC.
10091  *
10092  * Originally Released Under LGPL - original licence link has changed is not relivant.
10093  *
10094  * Fork - LGPL
10095  * <script type="text/javascript">
10096  */
10097 /**
10098  * @class Roo.data.HttpProxy
10099  * @extends Roo.data.DataProxy
10100  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10101  * configured to reference a certain URL.<br><br>
10102  * <p>
10103  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10104  * from which the running page was served.<br><br>
10105  * <p>
10106  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10107  * <p>
10108  * Be aware that to enable the browser to parse an XML document, the server must set
10109  * the Content-Type header in the HTTP response to "text/xml".
10110  * @constructor
10111  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10112  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10113  * will be used to make the request.
10114  */
10115 Roo.data.HttpProxy = function(conn){
10116     Roo.data.HttpProxy.superclass.constructor.call(this);
10117     // is conn a conn config or a real conn?
10118     this.conn = conn;
10119     this.useAjax = !conn || !conn.events;
10120   
10121 };
10122
10123 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10124     // thse are take from connection...
10125     
10126     /**
10127      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10128      */
10129     /**
10130      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10131      * extra parameters to each request made by this object. (defaults to undefined)
10132      */
10133     /**
10134      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10135      *  to each request made by this object. (defaults to undefined)
10136      */
10137     /**
10138      * @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)
10139      */
10140     /**
10141      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10142      */
10143      /**
10144      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10145      * @type Boolean
10146      */
10147   
10148
10149     /**
10150      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10151      * @type Boolean
10152      */
10153     /**
10154      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10155      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10156      * a finer-grained basis than the DataProxy events.
10157      */
10158     getConnection : function(){
10159         return this.useAjax ? Roo.Ajax : this.conn;
10160     },
10161
10162     /**
10163      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10164      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10165      * process that block using the passed callback.
10166      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10167      * for the request to the remote server.
10168      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10169      * object into a block of Roo.data.Records.
10170      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10171      * The function must be passed <ul>
10172      * <li>The Record block object</li>
10173      * <li>The "arg" argument from the load function</li>
10174      * <li>A boolean success indicator</li>
10175      * </ul>
10176      * @param {Object} scope The scope in which to call the callback
10177      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10178      */
10179     load : function(params, reader, callback, scope, arg){
10180         if(this.fireEvent("beforeload", this, params) !== false){
10181             var  o = {
10182                 params : params || {},
10183                 request: {
10184                     callback : callback,
10185                     scope : scope,
10186                     arg : arg
10187                 },
10188                 reader: reader,
10189                 callback : this.loadResponse,
10190                 scope: this
10191             };
10192             if(this.useAjax){
10193                 Roo.applyIf(o, this.conn);
10194                 if(this.activeRequest){
10195                     Roo.Ajax.abort(this.activeRequest);
10196                 }
10197                 this.activeRequest = Roo.Ajax.request(o);
10198             }else{
10199                 this.conn.request(o);
10200             }
10201         }else{
10202             callback.call(scope||this, null, arg, false);
10203         }
10204     },
10205
10206     // private
10207     loadResponse : function(o, success, response){
10208         delete this.activeRequest;
10209         if(!success){
10210             this.fireEvent("loadexception", this, o, response);
10211             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10212             return;
10213         }
10214         var result;
10215         try {
10216             result = o.reader.read(response);
10217         }catch(e){
10218             this.fireEvent("loadexception", this, o, response, e);
10219             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10220             return;
10221         }
10222         
10223         this.fireEvent("load", this, o, o.request.arg);
10224         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10225     },
10226
10227     // private
10228     update : function(dataSet){
10229
10230     },
10231
10232     // private
10233     updateResponse : function(dataSet){
10234
10235     }
10236 });/*
10237  * Based on:
10238  * Ext JS Library 1.1.1
10239  * Copyright(c) 2006-2007, Ext JS, LLC.
10240  *
10241  * Originally Released Under LGPL - original licence link has changed is not relivant.
10242  *
10243  * Fork - LGPL
10244  * <script type="text/javascript">
10245  */
10246
10247 /**
10248  * @class Roo.data.ScriptTagProxy
10249  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10250  * other than the originating domain of the running page.<br><br>
10251  * <p>
10252  * <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
10253  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10254  * <p>
10255  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10256  * source code that is used as the source inside a &lt;script> tag.<br><br>
10257  * <p>
10258  * In order for the browser to process the returned data, the server must wrap the data object
10259  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10260  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10261  * depending on whether the callback name was passed:
10262  * <p>
10263  * <pre><code>
10264 boolean scriptTag = false;
10265 String cb = request.getParameter("callback");
10266 if (cb != null) {
10267     scriptTag = true;
10268     response.setContentType("text/javascript");
10269 } else {
10270     response.setContentType("application/x-json");
10271 }
10272 Writer out = response.getWriter();
10273 if (scriptTag) {
10274     out.write(cb + "(");
10275 }
10276 out.print(dataBlock.toJsonString());
10277 if (scriptTag) {
10278     out.write(");");
10279 }
10280 </pre></code>
10281  *
10282  * @constructor
10283  * @param {Object} config A configuration object.
10284  */
10285 Roo.data.ScriptTagProxy = function(config){
10286     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10287     Roo.apply(this, config);
10288     this.head = document.getElementsByTagName("head")[0];
10289 };
10290
10291 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10292
10293 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10294     /**
10295      * @cfg {String} url The URL from which to request the data object.
10296      */
10297     /**
10298      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10299      */
10300     timeout : 30000,
10301     /**
10302      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10303      * the server the name of the callback function set up by the load call to process the returned data object.
10304      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10305      * javascript output which calls this named function passing the data object as its only parameter.
10306      */
10307     callbackParam : "callback",
10308     /**
10309      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10310      * name to the request.
10311      */
10312     nocache : true,
10313
10314     /**
10315      * Load data from the configured URL, read the data object into
10316      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10317      * process that block using the passed callback.
10318      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10319      * for the request to the remote server.
10320      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10321      * object into a block of Roo.data.Records.
10322      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10323      * The function must be passed <ul>
10324      * <li>The Record block object</li>
10325      * <li>The "arg" argument from the load function</li>
10326      * <li>A boolean success indicator</li>
10327      * </ul>
10328      * @param {Object} scope The scope in which to call the callback
10329      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10330      */
10331     load : function(params, reader, callback, scope, arg){
10332         if(this.fireEvent("beforeload", this, params) !== false){
10333
10334             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10335
10336             var url = this.url;
10337             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10338             if(this.nocache){
10339                 url += "&_dc=" + (new Date().getTime());
10340             }
10341             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10342             var trans = {
10343                 id : transId,
10344                 cb : "stcCallback"+transId,
10345                 scriptId : "stcScript"+transId,
10346                 params : params,
10347                 arg : arg,
10348                 url : url,
10349                 callback : callback,
10350                 scope : scope,
10351                 reader : reader
10352             };
10353             var conn = this;
10354
10355             window[trans.cb] = function(o){
10356                 conn.handleResponse(o, trans);
10357             };
10358
10359             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10360
10361             if(this.autoAbort !== false){
10362                 this.abort();
10363             }
10364
10365             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10366
10367             var script = document.createElement("script");
10368             script.setAttribute("src", url);
10369             script.setAttribute("type", "text/javascript");
10370             script.setAttribute("id", trans.scriptId);
10371             this.head.appendChild(script);
10372
10373             this.trans = trans;
10374         }else{
10375             callback.call(scope||this, null, arg, false);
10376         }
10377     },
10378
10379     // private
10380     isLoading : function(){
10381         return this.trans ? true : false;
10382     },
10383
10384     /**
10385      * Abort the current server request.
10386      */
10387     abort : function(){
10388         if(this.isLoading()){
10389             this.destroyTrans(this.trans);
10390         }
10391     },
10392
10393     // private
10394     destroyTrans : function(trans, isLoaded){
10395         this.head.removeChild(document.getElementById(trans.scriptId));
10396         clearTimeout(trans.timeoutId);
10397         if(isLoaded){
10398             window[trans.cb] = undefined;
10399             try{
10400                 delete window[trans.cb];
10401             }catch(e){}
10402         }else{
10403             // if hasn't been loaded, wait for load to remove it to prevent script error
10404             window[trans.cb] = function(){
10405                 window[trans.cb] = undefined;
10406                 try{
10407                     delete window[trans.cb];
10408                 }catch(e){}
10409             };
10410         }
10411     },
10412
10413     // private
10414     handleResponse : function(o, trans){
10415         this.trans = false;
10416         this.destroyTrans(trans, true);
10417         var result;
10418         try {
10419             result = trans.reader.readRecords(o);
10420         }catch(e){
10421             this.fireEvent("loadexception", this, o, trans.arg, e);
10422             trans.callback.call(trans.scope||window, null, trans.arg, false);
10423             return;
10424         }
10425         this.fireEvent("load", this, o, trans.arg);
10426         trans.callback.call(trans.scope||window, result, trans.arg, true);
10427     },
10428
10429     // private
10430     handleFailure : function(trans){
10431         this.trans = false;
10432         this.destroyTrans(trans, false);
10433         this.fireEvent("loadexception", this, null, trans.arg);
10434         trans.callback.call(trans.scope||window, null, trans.arg, false);
10435     }
10436 });/*
10437  * Based on:
10438  * Ext JS Library 1.1.1
10439  * Copyright(c) 2006-2007, Ext JS, LLC.
10440  *
10441  * Originally Released Under LGPL - original licence link has changed is not relivant.
10442  *
10443  * Fork - LGPL
10444  * <script type="text/javascript">
10445  */
10446
10447 /**
10448  * @class Roo.data.JsonReader
10449  * @extends Roo.data.DataReader
10450  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10451  * based on mappings in a provided Roo.data.Record constructor.
10452  * 
10453  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10454  * in the reply previously. 
10455  * 
10456  * <p>
10457  * Example code:
10458  * <pre><code>
10459 var RecordDef = Roo.data.Record.create([
10460     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10461     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10462 ]);
10463 var myReader = new Roo.data.JsonReader({
10464     totalProperty: "results",    // The property which contains the total dataset size (optional)
10465     root: "rows",                // The property which contains an Array of row objects
10466     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10467 }, RecordDef);
10468 </code></pre>
10469  * <p>
10470  * This would consume a JSON file like this:
10471  * <pre><code>
10472 { 'results': 2, 'rows': [
10473     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10474     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10475 }
10476 </code></pre>
10477  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10478  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10479  * paged from the remote server.
10480  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10481  * @cfg {String} root name of the property which contains the Array of row objects.
10482  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10483  * @constructor
10484  * Create a new JsonReader
10485  * @param {Object} meta Metadata configuration options
10486  * @param {Object} recordType Either an Array of field definition objects,
10487  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10488  */
10489 Roo.data.JsonReader = function(meta, recordType){
10490     
10491     meta = meta || {};
10492     // set some defaults:
10493     Roo.applyIf(meta, {
10494         totalProperty: 'total',
10495         successProperty : 'success',
10496         root : 'data',
10497         id : 'id'
10498     });
10499     
10500     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10501 };
10502 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10503     
10504     /**
10505      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10506      * Used by Store query builder to append _requestMeta to params.
10507      * 
10508      */
10509     metaFromRemote : false,
10510     /**
10511      * This method is only used by a DataProxy which has retrieved data from a remote server.
10512      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10513      * @return {Object} data A data block which is used by an Roo.data.Store object as
10514      * a cache of Roo.data.Records.
10515      */
10516     read : function(response){
10517         var json = response.responseText;
10518        
10519         var o = /* eval:var:o */ eval("("+json+")");
10520         if(!o) {
10521             throw {message: "JsonReader.read: Json object not found"};
10522         }
10523         
10524         if(o.metaData){
10525             
10526             delete this.ef;
10527             this.metaFromRemote = true;
10528             this.meta = o.metaData;
10529             this.recordType = Roo.data.Record.create(o.metaData.fields);
10530             this.onMetaChange(this.meta, this.recordType, o);
10531         }
10532         return this.readRecords(o);
10533     },
10534
10535     // private function a store will implement
10536     onMetaChange : function(meta, recordType, o){
10537
10538     },
10539
10540     /**
10541          * @ignore
10542          */
10543     simpleAccess: function(obj, subsc) {
10544         return obj[subsc];
10545     },
10546
10547         /**
10548          * @ignore
10549          */
10550     getJsonAccessor: function(){
10551         var re = /[\[\.]/;
10552         return function(expr) {
10553             try {
10554                 return(re.test(expr))
10555                     ? new Function("obj", "return obj." + expr)
10556                     : function(obj){
10557                         return obj[expr];
10558                     };
10559             } catch(e){}
10560             return Roo.emptyFn;
10561         };
10562     }(),
10563
10564     /**
10565      * Create a data block containing Roo.data.Records from an XML document.
10566      * @param {Object} o An object which contains an Array of row objects in the property specified
10567      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10568      * which contains the total size of the dataset.
10569      * @return {Object} data A data block which is used by an Roo.data.Store object as
10570      * a cache of Roo.data.Records.
10571      */
10572     readRecords : function(o){
10573         /**
10574          * After any data loads, the raw JSON data is available for further custom processing.
10575          * @type Object
10576          */
10577         this.o = o;
10578         var s = this.meta, Record = this.recordType,
10579             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10580
10581 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10582         if (!this.ef) {
10583             if(s.totalProperty) {
10584                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10585                 }
10586                 if(s.successProperty) {
10587                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10588                 }
10589                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10590                 if (s.id) {
10591                         var g = this.getJsonAccessor(s.id);
10592                         this.getId = function(rec) {
10593                                 var r = g(rec);  
10594                                 return (r === undefined || r === "") ? null : r;
10595                         };
10596                 } else {
10597                         this.getId = function(){return null;};
10598                 }
10599             this.ef = [];
10600             for(var jj = 0; jj < fl; jj++){
10601                 f = fi[jj];
10602                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10603                 this.ef[jj] = this.getJsonAccessor(map);
10604             }
10605         }
10606
10607         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10608         if(s.totalProperty){
10609             var vt = parseInt(this.getTotal(o), 10);
10610             if(!isNaN(vt)){
10611                 totalRecords = vt;
10612             }
10613         }
10614         if(s.successProperty){
10615             var vs = this.getSuccess(o);
10616             if(vs === false || vs === 'false'){
10617                 success = false;
10618             }
10619         }
10620         var records = [];
10621         for(var i = 0; i < c; i++){
10622                 var n = root[i];
10623             var values = {};
10624             var id = this.getId(n);
10625             for(var j = 0; j < fl; j++){
10626                 f = fi[j];
10627             var v = this.ef[j](n);
10628             if (!f.convert) {
10629                 Roo.log('missing convert for ' + f.name);
10630                 Roo.log(f);
10631                 continue;
10632             }
10633             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10634             }
10635             var record = new Record(values, id);
10636             record.json = n;
10637             records[i] = record;
10638         }
10639         return {
10640             raw : o,
10641             success : success,
10642             records : records,
10643             totalRecords : totalRecords
10644         };
10645     }
10646 });/*
10647  * Based on:
10648  * Ext JS Library 1.1.1
10649  * Copyright(c) 2006-2007, Ext JS, LLC.
10650  *
10651  * Originally Released Under LGPL - original licence link has changed is not relivant.
10652  *
10653  * Fork - LGPL
10654  * <script type="text/javascript">
10655  */
10656
10657 /**
10658  * @class Roo.data.ArrayReader
10659  * @extends Roo.data.DataReader
10660  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10661  * Each element of that Array represents a row of data fields. The
10662  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10663  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10664  * <p>
10665  * Example code:.
10666  * <pre><code>
10667 var RecordDef = Roo.data.Record.create([
10668     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10669     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10670 ]);
10671 var myReader = new Roo.data.ArrayReader({
10672     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10673 }, RecordDef);
10674 </code></pre>
10675  * <p>
10676  * This would consume an Array like this:
10677  * <pre><code>
10678 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10679   </code></pre>
10680  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10681  * @constructor
10682  * Create a new JsonReader
10683  * @param {Object} meta Metadata configuration options.
10684  * @param {Object} recordType Either an Array of field definition objects
10685  * as specified to {@link Roo.data.Record#create},
10686  * or an {@link Roo.data.Record} object
10687  * created using {@link Roo.data.Record#create}.
10688  */
10689 Roo.data.ArrayReader = function(meta, recordType){
10690     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10691 };
10692
10693 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10694     /**
10695      * Create a data block containing Roo.data.Records from an XML document.
10696      * @param {Object} o An Array of row objects which represents the dataset.
10697      * @return {Object} data A data block which is used by an Roo.data.Store object as
10698      * a cache of Roo.data.Records.
10699      */
10700     readRecords : function(o){
10701         var sid = this.meta ? this.meta.id : null;
10702         var recordType = this.recordType, fields = recordType.prototype.fields;
10703         var records = [];
10704         var root = o;
10705             for(var i = 0; i < root.length; i++){
10706                     var n = root[i];
10707                 var values = {};
10708                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10709                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10710                 var f = fields.items[j];
10711                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10712                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10713                 v = f.convert(v);
10714                 values[f.name] = v;
10715             }
10716                 var record = new recordType(values, id);
10717                 record.json = n;
10718                 records[records.length] = record;
10719             }
10720             return {
10721                 records : records,
10722                 totalRecords : records.length
10723             };
10724     }
10725 });/*
10726  * - LGPL
10727  * * 
10728  */
10729
10730 /**
10731  * @class Roo.bootstrap.ComboBox
10732  * @extends Roo.bootstrap.TriggerField
10733  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10734  * @cfg {Boolean} append (true|false) default false
10735  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10736  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10737  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10738  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10739  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10740  * @constructor
10741  * Create a new ComboBox.
10742  * @param {Object} config Configuration options
10743  */
10744 Roo.bootstrap.ComboBox = function(config){
10745     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10746     this.addEvents({
10747         /**
10748          * @event expand
10749          * Fires when the dropdown list is expanded
10750              * @param {Roo.bootstrap.ComboBox} combo This combo box
10751              */
10752         'expand' : true,
10753         /**
10754          * @event collapse
10755          * Fires when the dropdown list is collapsed
10756              * @param {Roo.bootstrap.ComboBox} combo This combo box
10757              */
10758         'collapse' : true,
10759         /**
10760          * @event beforeselect
10761          * Fires before a list item is selected. Return false to cancel the selection.
10762              * @param {Roo.bootstrap.ComboBox} combo This combo box
10763              * @param {Roo.data.Record} record The data record returned from the underlying store
10764              * @param {Number} index The index of the selected item in the dropdown list
10765              */
10766         'beforeselect' : true,
10767         /**
10768          * @event select
10769          * Fires when a list item is selected
10770              * @param {Roo.bootstrap.ComboBox} combo This combo box
10771              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10772              * @param {Number} index The index of the selected item in the dropdown list
10773              */
10774         'select' : true,
10775         /**
10776          * @event beforequery
10777          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10778          * The event object passed has these properties:
10779              * @param {Roo.bootstrap.ComboBox} combo This combo box
10780              * @param {String} query The query
10781              * @param {Boolean} forceAll true to force "all" query
10782              * @param {Boolean} cancel true to cancel the query
10783              * @param {Object} e The query event object
10784              */
10785         'beforequery': true,
10786          /**
10787          * @event add
10788          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10789              * @param {Roo.bootstrap.ComboBox} combo This combo box
10790              */
10791         'add' : true,
10792         /**
10793          * @event edit
10794          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10795              * @param {Roo.bootstrap.ComboBox} combo This combo box
10796              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10797              */
10798         'edit' : true,
10799         /**
10800          * @event remove
10801          * Fires when the remove value from the combobox array
10802              * @param {Roo.bootstrap.ComboBox} combo This combo box
10803              */
10804         'remove' : true,
10805         /**
10806          * @event specialfilter
10807          * Fires when specialfilter
10808             * @param {Roo.bootstrap.ComboBox} combo This combo box
10809             */
10810         'specialfilter' : true
10811         
10812     });
10813     
10814     this.item = [];
10815     this.tickItems = [];
10816     
10817     this.selectedIndex = -1;
10818     if(this.mode == 'local'){
10819         if(config.queryDelay === undefined){
10820             this.queryDelay = 10;
10821         }
10822         if(config.minChars === undefined){
10823             this.minChars = 0;
10824         }
10825     }
10826 };
10827
10828 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10829      
10830     /**
10831      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10832      * rendering into an Roo.Editor, defaults to false)
10833      */
10834     /**
10835      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10836      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10837      */
10838     /**
10839      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10840      */
10841     /**
10842      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10843      * the dropdown list (defaults to undefined, with no header element)
10844      */
10845
10846      /**
10847      * @cfg {String/Roo.Template} tpl The template to use to render the output
10848      */
10849      
10850      /**
10851      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10852      */
10853     listWidth: undefined,
10854     /**
10855      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10856      * mode = 'remote' or 'text' if mode = 'local')
10857      */
10858     displayField: undefined,
10859     
10860     /**
10861      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10862      * mode = 'remote' or 'value' if mode = 'local'). 
10863      * Note: use of a valueField requires the user make a selection
10864      * in order for a value to be mapped.
10865      */
10866     valueField: undefined,
10867     
10868     
10869     /**
10870      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10871      * field's data value (defaults to the underlying DOM element's name)
10872      */
10873     hiddenName: undefined,
10874     /**
10875      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10876      */
10877     listClass: '',
10878     /**
10879      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10880      */
10881     selectedClass: 'active',
10882     
10883     /**
10884      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10885      */
10886     shadow:'sides',
10887     /**
10888      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10889      * anchor positions (defaults to 'tl-bl')
10890      */
10891     listAlign: 'tl-bl?',
10892     /**
10893      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10894      */
10895     maxHeight: 300,
10896     /**
10897      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10898      * query specified by the allQuery config option (defaults to 'query')
10899      */
10900     triggerAction: 'query',
10901     /**
10902      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10903      * (defaults to 4, does not apply if editable = false)
10904      */
10905     minChars : 4,
10906     /**
10907      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10908      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10909      */
10910     typeAhead: false,
10911     /**
10912      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10913      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10914      */
10915     queryDelay: 500,
10916     /**
10917      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10918      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10919      */
10920     pageSize: 0,
10921     /**
10922      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10923      * when editable = true (defaults to false)
10924      */
10925     selectOnFocus:false,
10926     /**
10927      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10928      */
10929     queryParam: 'query',
10930     /**
10931      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10932      * when mode = 'remote' (defaults to 'Loading...')
10933      */
10934     loadingText: 'Loading...',
10935     /**
10936      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10937      */
10938     resizable: false,
10939     /**
10940      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10941      */
10942     handleHeight : 8,
10943     /**
10944      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10945      * traditional select (defaults to true)
10946      */
10947     editable: true,
10948     /**
10949      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10950      */
10951     allQuery: '',
10952     /**
10953      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10954      */
10955     mode: 'remote',
10956     /**
10957      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10958      * listWidth has a higher value)
10959      */
10960     minListWidth : 70,
10961     /**
10962      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10963      * allow the user to set arbitrary text into the field (defaults to false)
10964      */
10965     forceSelection:false,
10966     /**
10967      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10968      * if typeAhead = true (defaults to 250)
10969      */
10970     typeAheadDelay : 250,
10971     /**
10972      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10973      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10974      */
10975     valueNotFoundText : undefined,
10976     /**
10977      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10978      */
10979     blockFocus : false,
10980     
10981     /**
10982      * @cfg {Boolean} disableClear Disable showing of clear button.
10983      */
10984     disableClear : false,
10985     /**
10986      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10987      */
10988     alwaysQuery : false,
10989     
10990     /**
10991      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10992      */
10993     multiple : false,
10994     
10995     /**
10996      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10997      */
10998     invalidClass : "has-warning",
10999     
11000     /**
11001      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11002      */
11003     validClass : "has-success",
11004     
11005     /**
11006      * @cfg {Boolean} specialFilter (true|false) special filter default false
11007      */
11008     specialFilter : false,
11009     
11010     //private
11011     addicon : false,
11012     editicon: false,
11013     
11014     page: 0,
11015     hasQuery: false,
11016     append: false,
11017     loadNext: false,
11018     autoFocus : true,
11019     tickable : false,
11020     btnPosition : 'right',
11021     triggerList : true,
11022     showToggleBtn : true,
11023     // element that contains real text value.. (when hidden is used..)
11024     
11025     getAutoCreate : function()
11026     {
11027         var cfg = false;
11028         
11029         /*
11030          *  Normal ComboBox
11031          */
11032         if(!this.tickable){
11033             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11034             return cfg;
11035         }
11036         
11037         /*
11038          *  ComboBox with tickable selections
11039          */
11040              
11041         var align = this.labelAlign || this.parentLabelAlign();
11042         
11043         cfg = {
11044             cls : 'form-group roo-combobox-tickable' //input-group
11045         };
11046         
11047         var buttons = {
11048             tag : 'div',
11049             cls : 'tickable-buttons',
11050             cn : [
11051                 {
11052                     tag : 'button',
11053                     type : 'button',
11054                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11055                     html : 'Edit'
11056                 },
11057                 {
11058                     tag : 'button',
11059                     type : 'button',
11060                     name : 'ok',
11061                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11062                     html : 'Done'
11063                 },
11064                 {
11065                     tag : 'button',
11066                     type : 'button',
11067                     name : 'cancel',
11068                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11069                     html : 'Cancel'
11070                 }
11071             ]
11072         };
11073         
11074         if(this.editable){
11075             buttons.cn.unshift({
11076                 tag: 'input',
11077                 cls: 'select2-search-field-input'
11078             });
11079         }
11080         
11081         var _this = this;
11082         
11083         Roo.each(buttons.cn, function(c){
11084             if (_this.size) {
11085                 c.cls += ' btn-' + _this.size;
11086             }
11087
11088             if (_this.disabled) {
11089                 c.disabled = true;
11090             }
11091         });
11092         
11093         var box = {
11094             tag: 'div',
11095             cn: [
11096                 {
11097                     tag: 'input',
11098                     type : 'hidden',
11099                     cls: 'form-hidden-field'
11100                 },
11101                 {
11102                     tag: 'ul',
11103                     cls: 'select2-choices',
11104                     cn:[
11105                         {
11106                             tag: 'li',
11107                             cls: 'select2-search-field',
11108                             cn: [
11109
11110                                 buttons
11111                             ]
11112                         }
11113                     ]
11114                 }
11115             ]
11116         }
11117         
11118         var combobox = {
11119             cls: 'select2-container input-group select2-container-multi',
11120             cn: [
11121                 box
11122 //                {
11123 //                    tag: 'ul',
11124 //                    cls: 'typeahead typeahead-long dropdown-menu',
11125 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11126 //                }
11127             ]
11128         };
11129         
11130         if(this.hasFeedback && !this.allowBlank){
11131             
11132             var feedback = {
11133                 tag: 'span',
11134                 cls: 'glyphicon form-control-feedback'
11135             };
11136
11137             combobox.cn.push(feedback);
11138         }
11139         
11140         if (align ==='left' && this.fieldLabel.length) {
11141             
11142                 Roo.log("left and has label");
11143                 cfg.cn = [
11144                     
11145                     {
11146                         tag: 'label',
11147                         'for' :  id,
11148                         cls : 'control-label col-sm-' + this.labelWidth,
11149                         html : this.fieldLabel
11150                         
11151                     },
11152                     {
11153                         cls : "col-sm-" + (12 - this.labelWidth), 
11154                         cn: [
11155                             combobox
11156                         ]
11157                     }
11158                     
11159                 ];
11160         } else if ( this.fieldLabel.length) {
11161                 Roo.log(" label");
11162                  cfg.cn = [
11163                    
11164                     {
11165                         tag: 'label',
11166                         //cls : 'input-group-addon',
11167                         html : this.fieldLabel
11168                         
11169                     },
11170                     
11171                     combobox
11172                     
11173                 ];
11174
11175         } else {
11176             
11177                 Roo.log(" no label && no align");
11178                 cfg = combobox
11179                      
11180                 
11181         }
11182          
11183         var settings=this;
11184         ['xs','sm','md','lg'].map(function(size){
11185             if (settings[size]) {
11186                 cfg.cls += ' col-' + size + '-' + settings[size];
11187             }
11188         });
11189         
11190         return cfg;
11191         
11192     },
11193     
11194     // private
11195     initEvents: function()
11196     {
11197         
11198         if (!this.store) {
11199             throw "can not find store for combo";
11200         }
11201         this.store = Roo.factory(this.store, Roo.data);
11202         
11203         if(this.tickable){
11204             this.initTickableEvents();
11205             return;
11206         }
11207         
11208         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11209         
11210         if(this.hiddenName){
11211             
11212             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11213             
11214             this.hiddenField.dom.value =
11215                 this.hiddenValue !== undefined ? this.hiddenValue :
11216                 this.value !== undefined ? this.value : '';
11217
11218             // prevent input submission
11219             this.el.dom.removeAttribute('name');
11220             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11221              
11222              
11223         }
11224         //if(Roo.isGecko){
11225         //    this.el.dom.setAttribute('autocomplete', 'off');
11226         //}
11227         
11228         var cls = 'x-combo-list';
11229         
11230         //this.list = new Roo.Layer({
11231         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11232         //});
11233         
11234         var _this = this;
11235         
11236         (function(){
11237             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11238             _this.list.setWidth(lw);
11239         }).defer(100);
11240         
11241         this.list.on('mouseover', this.onViewOver, this);
11242         this.list.on('mousemove', this.onViewMove, this);
11243         
11244         this.list.on('scroll', this.onViewScroll, this);
11245         
11246         /*
11247         this.list.swallowEvent('mousewheel');
11248         this.assetHeight = 0;
11249
11250         if(this.title){
11251             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11252             this.assetHeight += this.header.getHeight();
11253         }
11254
11255         this.innerList = this.list.createChild({cls:cls+'-inner'});
11256         this.innerList.on('mouseover', this.onViewOver, this);
11257         this.innerList.on('mousemove', this.onViewMove, this);
11258         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11259         
11260         if(this.allowBlank && !this.pageSize && !this.disableClear){
11261             this.footer = this.list.createChild({cls:cls+'-ft'});
11262             this.pageTb = new Roo.Toolbar(this.footer);
11263            
11264         }
11265         if(this.pageSize){
11266             this.footer = this.list.createChild({cls:cls+'-ft'});
11267             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11268                     {pageSize: this.pageSize});
11269             
11270         }
11271         
11272         if (this.pageTb && this.allowBlank && !this.disableClear) {
11273             var _this = this;
11274             this.pageTb.add(new Roo.Toolbar.Fill(), {
11275                 cls: 'x-btn-icon x-btn-clear',
11276                 text: '&#160;',
11277                 handler: function()
11278                 {
11279                     _this.collapse();
11280                     _this.clearValue();
11281                     _this.onSelect(false, -1);
11282                 }
11283             });
11284         }
11285         if (this.footer) {
11286             this.assetHeight += this.footer.getHeight();
11287         }
11288         */
11289             
11290         if(!this.tpl){
11291             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11292         }
11293
11294         this.view = new Roo.View(this.list, this.tpl, {
11295             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11296         });
11297         //this.view.wrapEl.setDisplayed(false);
11298         this.view.on('click', this.onViewClick, this);
11299         
11300         
11301         
11302         this.store.on('beforeload', this.onBeforeLoad, this);
11303         this.store.on('load', this.onLoad, this);
11304         this.store.on('loadexception', this.onLoadException, this);
11305         /*
11306         if(this.resizable){
11307             this.resizer = new Roo.Resizable(this.list,  {
11308                pinned:true, handles:'se'
11309             });
11310             this.resizer.on('resize', function(r, w, h){
11311                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11312                 this.listWidth = w;
11313                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11314                 this.restrictHeight();
11315             }, this);
11316             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11317         }
11318         */
11319         if(!this.editable){
11320             this.editable = true;
11321             this.setEditable(false);
11322         }
11323         
11324         /*
11325         
11326         if (typeof(this.events.add.listeners) != 'undefined') {
11327             
11328             this.addicon = this.wrap.createChild(
11329                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11330        
11331             this.addicon.on('click', function(e) {
11332                 this.fireEvent('add', this);
11333             }, this);
11334         }
11335         if (typeof(this.events.edit.listeners) != 'undefined') {
11336             
11337             this.editicon = this.wrap.createChild(
11338                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11339             if (this.addicon) {
11340                 this.editicon.setStyle('margin-left', '40px');
11341             }
11342             this.editicon.on('click', function(e) {
11343                 
11344                 // we fire even  if inothing is selected..
11345                 this.fireEvent('edit', this, this.lastData );
11346                 
11347             }, this);
11348         }
11349         */
11350         
11351         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11352             "up" : function(e){
11353                 this.inKeyMode = true;
11354                 this.selectPrev();
11355             },
11356
11357             "down" : function(e){
11358                 if(!this.isExpanded()){
11359                     this.onTriggerClick();
11360                 }else{
11361                     this.inKeyMode = true;
11362                     this.selectNext();
11363                 }
11364             },
11365
11366             "enter" : function(e){
11367 //                this.onViewClick();
11368                 //return true;
11369                 this.collapse();
11370                 
11371                 if(this.fireEvent("specialkey", this, e)){
11372                     this.onViewClick(false);
11373                 }
11374                 
11375                 return true;
11376             },
11377
11378             "esc" : function(e){
11379                 this.collapse();
11380             },
11381
11382             "tab" : function(e){
11383                 this.collapse();
11384                 
11385                 if(this.fireEvent("specialkey", this, e)){
11386                     this.onViewClick(false);
11387                 }
11388                 
11389                 return true;
11390             },
11391
11392             scope : this,
11393
11394             doRelay : function(foo, bar, hname){
11395                 if(hname == 'down' || this.scope.isExpanded()){
11396                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11397                 }
11398                 return true;
11399             },
11400
11401             forceKeyDown: true
11402         });
11403         
11404         
11405         this.queryDelay = Math.max(this.queryDelay || 10,
11406                 this.mode == 'local' ? 10 : 250);
11407         
11408         
11409         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11410         
11411         if(this.typeAhead){
11412             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11413         }
11414         if(this.editable !== false){
11415             this.inputEl().on("keyup", this.onKeyUp, this);
11416         }
11417         if(this.forceSelection){
11418             this.inputEl().on('blur', this.doForce, this);
11419         }
11420         
11421         if(this.multiple){
11422             this.choices = this.el.select('ul.select2-choices', true).first();
11423             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11424         }
11425     },
11426     
11427     initTickableEvents: function()
11428     {   
11429         this.createList();
11430         
11431         if(this.hiddenName){
11432             
11433             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11434             
11435             this.hiddenField.dom.value =
11436                 this.hiddenValue !== undefined ? this.hiddenValue :
11437                 this.value !== undefined ? this.value : '';
11438
11439             // prevent input submission
11440             this.el.dom.removeAttribute('name');
11441             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11442              
11443              
11444         }
11445         
11446 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11447         
11448         this.choices = this.el.select('ul.select2-choices', true).first();
11449         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11450         if(this.triggerList){
11451             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11452         }
11453          
11454         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11455         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11456         
11457         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11458         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11459         
11460         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11461         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11462         
11463         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11464         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11465         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11466         
11467         this.okBtn.hide();
11468         this.cancelBtn.hide();
11469         
11470         var _this = this;
11471         
11472         (function(){
11473             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11474             _this.list.setWidth(lw);
11475         }).defer(100);
11476         
11477         this.list.on('mouseover', this.onViewOver, this);
11478         this.list.on('mousemove', this.onViewMove, this);
11479         
11480         this.list.on('scroll', this.onViewScroll, this);
11481         
11482         if(!this.tpl){
11483             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>';
11484         }
11485
11486         this.view = new Roo.View(this.list, this.tpl, {
11487             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11488         });
11489         
11490         //this.view.wrapEl.setDisplayed(false);
11491         this.view.on('click', this.onViewClick, this);
11492         
11493         
11494         
11495         this.store.on('beforeload', this.onBeforeLoad, this);
11496         this.store.on('load', this.onLoad, this);
11497         this.store.on('loadexception', this.onLoadException, this);
11498         
11499         if(this.editable){
11500             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11501                 "up" : function(e){
11502                     this.inKeyMode = true;
11503                     this.selectPrev();
11504                 },
11505
11506                 "down" : function(e){
11507                     this.inKeyMode = true;
11508                     this.selectNext();
11509                 },
11510
11511                 "enter" : function(e){
11512                     if(this.fireEvent("specialkey", this, e)){
11513                         this.onViewClick(false);
11514                     }
11515                     
11516                     return true;
11517                 },
11518
11519                 "esc" : function(e){
11520                     this.onTickableFooterButtonClick(e, false, false);
11521                 },
11522
11523                 "tab" : function(e){
11524                     this.fireEvent("specialkey", this, e);
11525                     
11526                     this.onTickableFooterButtonClick(e, false, false);
11527                     
11528                     return true;
11529                 },
11530
11531                 scope : this,
11532
11533                 doRelay : function(e, fn, key){
11534                     if(this.scope.isExpanded()){
11535                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11536                     }
11537                     return true;
11538                 },
11539
11540                 forceKeyDown: true
11541             });
11542         }
11543         
11544         this.queryDelay = Math.max(this.queryDelay || 10,
11545                 this.mode == 'local' ? 10 : 250);
11546         
11547         
11548         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11549         
11550         if(this.typeAhead){
11551             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11552         }
11553         
11554         if(this.editable !== false){
11555             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11556         }
11557         
11558     },
11559
11560     onDestroy : function(){
11561         if(this.view){
11562             this.view.setStore(null);
11563             this.view.el.removeAllListeners();
11564             this.view.el.remove();
11565             this.view.purgeListeners();
11566         }
11567         if(this.list){
11568             this.list.dom.innerHTML  = '';
11569         }
11570         
11571         if(this.store){
11572             this.store.un('beforeload', this.onBeforeLoad, this);
11573             this.store.un('load', this.onLoad, this);
11574             this.store.un('loadexception', this.onLoadException, this);
11575         }
11576         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11577     },
11578
11579     // private
11580     fireKey : function(e){
11581         if(e.isNavKeyPress() && !this.list.isVisible()){
11582             this.fireEvent("specialkey", this, e);
11583         }
11584     },
11585
11586     // private
11587     onResize: function(w, h){
11588 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11589 //        
11590 //        if(typeof w != 'number'){
11591 //            // we do not handle it!?!?
11592 //            return;
11593 //        }
11594 //        var tw = this.trigger.getWidth();
11595 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11596 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11597 //        var x = w - tw;
11598 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11599 //            
11600 //        //this.trigger.setStyle('left', x+'px');
11601 //        
11602 //        if(this.list && this.listWidth === undefined){
11603 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11604 //            this.list.setWidth(lw);
11605 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11606 //        }
11607         
11608     
11609         
11610     },
11611
11612     /**
11613      * Allow or prevent the user from directly editing the field text.  If false is passed,
11614      * the user will only be able to select from the items defined in the dropdown list.  This method
11615      * is the runtime equivalent of setting the 'editable' config option at config time.
11616      * @param {Boolean} value True to allow the user to directly edit the field text
11617      */
11618     setEditable : function(value){
11619         if(value == this.editable){
11620             return;
11621         }
11622         this.editable = value;
11623         if(!value){
11624             this.inputEl().dom.setAttribute('readOnly', true);
11625             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11626             this.inputEl().addClass('x-combo-noedit');
11627         }else{
11628             this.inputEl().dom.setAttribute('readOnly', false);
11629             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11630             this.inputEl().removeClass('x-combo-noedit');
11631         }
11632     },
11633
11634     // private
11635     
11636     onBeforeLoad : function(combo,opts){
11637         if(!this.hasFocus){
11638             return;
11639         }
11640          if (!opts.add) {
11641             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11642          }
11643         this.restrictHeight();
11644         this.selectedIndex = -1;
11645     },
11646
11647     // private
11648     onLoad : function(){
11649         
11650         this.hasQuery = false;
11651         
11652         if(!this.hasFocus){
11653             return;
11654         }
11655         
11656         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11657             this.loading.hide();
11658         }
11659              
11660         if(this.store.getCount() > 0){
11661             this.expand();
11662             this.restrictHeight();
11663             if(this.lastQuery == this.allQuery){
11664                 if(this.editable && !this.tickable){
11665                     this.inputEl().dom.select();
11666                 }
11667                 
11668                 if(
11669                     !this.selectByValue(this.value, true) &&
11670                     this.autoFocus && 
11671                     (
11672                         !this.store.lastOptions ||
11673                         typeof(this.store.lastOptions.add) == 'undefined' || 
11674                         this.store.lastOptions.add != true
11675                     )
11676                 ){
11677                     this.select(0, true);
11678                 }
11679             }else{
11680                 if(this.autoFocus){
11681                     this.selectNext();
11682                 }
11683                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11684                     this.taTask.delay(this.typeAheadDelay);
11685                 }
11686             }
11687         }else{
11688             this.onEmptyResults();
11689         }
11690         
11691         //this.el.focus();
11692     },
11693     // private
11694     onLoadException : function()
11695     {
11696         this.hasQuery = false;
11697         
11698         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11699             this.loading.hide();
11700         }
11701         
11702         if(this.tickable && this.editable){
11703             return;
11704         }
11705         
11706         this.collapse();
11707         
11708         Roo.log(this.store.reader.jsonData);
11709         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11710             // fixme
11711             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11712         }
11713         
11714         
11715     },
11716     // private
11717     onTypeAhead : function(){
11718         if(this.store.getCount() > 0){
11719             var r = this.store.getAt(0);
11720             var newValue = r.data[this.displayField];
11721             var len = newValue.length;
11722             var selStart = this.getRawValue().length;
11723             
11724             if(selStart != len){
11725                 this.setRawValue(newValue);
11726                 this.selectText(selStart, newValue.length);
11727             }
11728         }
11729     },
11730
11731     // private
11732     onSelect : function(record, index){
11733         
11734         if(this.fireEvent('beforeselect', this, record, index) !== false){
11735         
11736             this.setFromData(index > -1 ? record.data : false);
11737             
11738             this.collapse();
11739             this.fireEvent('select', this, record, index);
11740         }
11741     },
11742
11743     /**
11744      * Returns the currently selected field value or empty string if no value is set.
11745      * @return {String} value The selected value
11746      */
11747     getValue : function(){
11748         
11749         if(this.multiple){
11750             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11751         }
11752         
11753         if(this.valueField){
11754             return typeof this.value != 'undefined' ? this.value : '';
11755         }else{
11756             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11757         }
11758     },
11759
11760     /**
11761      * Clears any text/value currently set in the field
11762      */
11763     clearValue : function(){
11764         if(this.hiddenField){
11765             this.hiddenField.dom.value = '';
11766         }
11767         this.value = '';
11768         this.setRawValue('');
11769         this.lastSelectionText = '';
11770         this.lastData = false;
11771         
11772     },
11773
11774     /**
11775      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11776      * will be displayed in the field.  If the value does not match the data value of an existing item,
11777      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11778      * Otherwise the field will be blank (although the value will still be set).
11779      * @param {String} value The value to match
11780      */
11781     setValue : function(v){
11782         if(this.multiple){
11783             this.syncValue();
11784             return;
11785         }
11786         
11787         var text = v;
11788         if(this.valueField){
11789             var r = this.findRecord(this.valueField, v);
11790             if(r){
11791                 text = r.data[this.displayField];
11792             }else if(this.valueNotFoundText !== undefined){
11793                 text = this.valueNotFoundText;
11794             }
11795         }
11796         this.lastSelectionText = text;
11797         if(this.hiddenField){
11798             this.hiddenField.dom.value = v;
11799         }
11800         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11801         this.value = v;
11802     },
11803     /**
11804      * @property {Object} the last set data for the element
11805      */
11806     
11807     lastData : false,
11808     /**
11809      * Sets the value of the field based on a object which is related to the record format for the store.
11810      * @param {Object} value the value to set as. or false on reset?
11811      */
11812     setFromData : function(o){
11813         
11814         if(this.multiple){
11815             this.addItem(o);
11816             return;
11817         }
11818             
11819         var dv = ''; // display value
11820         var vv = ''; // value value..
11821         this.lastData = o;
11822         if (this.displayField) {
11823             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11824         } else {
11825             // this is an error condition!!!
11826             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11827         }
11828         
11829         if(this.valueField){
11830             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11831         }
11832         
11833         if(this.hiddenField){
11834             this.hiddenField.dom.value = vv;
11835             
11836             this.lastSelectionText = dv;
11837             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11838             this.value = vv;
11839             return;
11840         }
11841         // no hidden field.. - we store the value in 'value', but still display
11842         // display field!!!!
11843         this.lastSelectionText = dv;
11844         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11845         this.value = vv;
11846         
11847         
11848     },
11849     // private
11850     reset : function(){
11851         // overridden so that last data is reset..
11852         
11853         if(this.multiple){
11854             this.clearItem();
11855             return;
11856         }
11857         
11858         this.setValue(this.originalValue);
11859         this.clearInvalid();
11860         this.lastData = false;
11861         if (this.view) {
11862             this.view.clearSelections();
11863         }
11864     },
11865     // private
11866     findRecord : function(prop, value){
11867         var record;
11868         if(this.store.getCount() > 0){
11869             this.store.each(function(r){
11870                 if(r.data[prop] == value){
11871                     record = r;
11872                     return false;
11873                 }
11874                 return true;
11875             });
11876         }
11877         return record;
11878     },
11879     
11880     getName: function()
11881     {
11882         // returns hidden if it's set..
11883         if (!this.rendered) {return ''};
11884         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11885         
11886     },
11887     // private
11888     onViewMove : function(e, t){
11889         this.inKeyMode = false;
11890     },
11891
11892     // private
11893     onViewOver : function(e, t){
11894         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11895             return;
11896         }
11897         var item = this.view.findItemFromChild(t);
11898         
11899         if(item){
11900             var index = this.view.indexOf(item);
11901             this.select(index, false);
11902         }
11903     },
11904
11905     // private
11906     onViewClick : function(view, doFocus, el, e)
11907     {
11908         var index = this.view.getSelectedIndexes()[0];
11909         
11910         var r = this.store.getAt(index);
11911         
11912         if(this.tickable){
11913             
11914             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11915                 return;
11916             }
11917             
11918             var rm = false;
11919             var _this = this;
11920             
11921             Roo.each(this.tickItems, function(v,k){
11922                 
11923                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11924                     _this.tickItems.splice(k, 1);
11925                     
11926                     if(typeof(e) == 'undefined' && view == false){
11927                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11928                     }
11929                     
11930                     rm = true;
11931                     return;
11932                 }
11933             });
11934             
11935             if(rm){
11936                 return;
11937             }
11938             
11939             this.tickItems.push(r.data);
11940             
11941             if(typeof(e) == 'undefined' && view == false){
11942                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11943             }
11944                     
11945             return;
11946         }
11947         
11948         if(r){
11949             this.onSelect(r, index);
11950         }
11951         if(doFocus !== false && !this.blockFocus){
11952             this.inputEl().focus();
11953         }
11954     },
11955
11956     // private
11957     restrictHeight : function(){
11958         //this.innerList.dom.style.height = '';
11959         //var inner = this.innerList.dom;
11960         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11961         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11962         //this.list.beginUpdate();
11963         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11964         this.list.alignTo(this.inputEl(), this.listAlign);
11965         this.list.alignTo(this.inputEl(), this.listAlign);
11966         //this.list.endUpdate();
11967     },
11968
11969     // private
11970     onEmptyResults : function(){
11971         
11972         if(this.tickable && this.editable){
11973             this.restrictHeight();
11974             return;
11975         }
11976         
11977         this.collapse();
11978     },
11979
11980     /**
11981      * Returns true if the dropdown list is expanded, else false.
11982      */
11983     isExpanded : function(){
11984         return this.list.isVisible();
11985     },
11986
11987     /**
11988      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11989      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11990      * @param {String} value The data value of the item to select
11991      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11992      * selected item if it is not currently in view (defaults to true)
11993      * @return {Boolean} True if the value matched an item in the list, else false
11994      */
11995     selectByValue : function(v, scrollIntoView){
11996         if(v !== undefined && v !== null){
11997             var r = this.findRecord(this.valueField || this.displayField, v);
11998             if(r){
11999                 this.select(this.store.indexOf(r), scrollIntoView);
12000                 return true;
12001             }
12002         }
12003         return false;
12004     },
12005
12006     /**
12007      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12008      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12009      * @param {Number} index The zero-based index of the list item to select
12010      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12011      * selected item if it is not currently in view (defaults to true)
12012      */
12013     select : function(index, scrollIntoView){
12014         this.selectedIndex = index;
12015         this.view.select(index);
12016         if(scrollIntoView !== false){
12017             var el = this.view.getNode(index);
12018             /*
12019              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12020              */
12021             if(el){
12022                 this.list.scrollChildIntoView(el, false);
12023             }
12024         }
12025     },
12026
12027     // private
12028     selectNext : function(){
12029         var ct = this.store.getCount();
12030         if(ct > 0){
12031             if(this.selectedIndex == -1){
12032                 this.select(0);
12033             }else if(this.selectedIndex < ct-1){
12034                 this.select(this.selectedIndex+1);
12035             }
12036         }
12037     },
12038
12039     // private
12040     selectPrev : function(){
12041         var ct = this.store.getCount();
12042         if(ct > 0){
12043             if(this.selectedIndex == -1){
12044                 this.select(0);
12045             }else if(this.selectedIndex != 0){
12046                 this.select(this.selectedIndex-1);
12047             }
12048         }
12049     },
12050
12051     // private
12052     onKeyUp : function(e){
12053         if(this.editable !== false && !e.isSpecialKey()){
12054             this.lastKey = e.getKey();
12055             this.dqTask.delay(this.queryDelay);
12056         }
12057     },
12058
12059     // private
12060     validateBlur : function(){
12061         return !this.list || !this.list.isVisible();   
12062     },
12063
12064     // private
12065     initQuery : function(){
12066         
12067         var v = this.getRawValue();
12068         
12069         if(this.tickable && this.editable){
12070             v = this.tickableInputEl().getValue();
12071         }
12072         
12073         this.doQuery(v);
12074     },
12075
12076     // private
12077     doForce : function(){
12078         if(this.inputEl().dom.value.length > 0){
12079             this.inputEl().dom.value =
12080                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12081              
12082         }
12083     },
12084
12085     /**
12086      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12087      * query allowing the query action to be canceled if needed.
12088      * @param {String} query The SQL query to execute
12089      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12090      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12091      * saved in the current store (defaults to false)
12092      */
12093     doQuery : function(q, forceAll){
12094         
12095         if(q === undefined || q === null){
12096             q = '';
12097         }
12098         var qe = {
12099             query: q,
12100             forceAll: forceAll,
12101             combo: this,
12102             cancel:false
12103         };
12104         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12105             return false;
12106         }
12107         q = qe.query;
12108         
12109         forceAll = qe.forceAll;
12110         if(forceAll === true || (q.length >= this.minChars)){
12111             
12112             this.hasQuery = true;
12113             
12114             if(this.lastQuery != q || this.alwaysQuery){
12115                 this.lastQuery = q;
12116                 if(this.mode == 'local'){
12117                     this.selectedIndex = -1;
12118                     if(forceAll){
12119                         this.store.clearFilter();
12120                     }else{
12121                         
12122                         if(this.specialFilter){
12123                             this.fireEvent('specialfilter', this);
12124                             this.onLoad();
12125                             return;
12126                         }
12127                         
12128                         this.store.filter(this.displayField, q);
12129                     }
12130                     
12131                     this.store.fireEvent("datachanged", this.store);
12132                     
12133                     this.onLoad();
12134                     
12135                     
12136                 }else{
12137                     
12138                     this.store.baseParams[this.queryParam] = q;
12139                     
12140                     var options = {params : this.getParams(q)};
12141                     
12142                     if(this.loadNext){
12143                         options.add = true;
12144                         options.params.start = this.page * this.pageSize;
12145                     }
12146                     
12147                     this.store.load(options);
12148                     
12149                     /*
12150                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12151                      *  we should expand the list on onLoad
12152                      *  so command out it
12153                      */
12154 //                    this.expand();
12155                 }
12156             }else{
12157                 this.selectedIndex = -1;
12158                 this.onLoad();   
12159             }
12160         }
12161         
12162         this.loadNext = false;
12163     },
12164     
12165     // private
12166     getParams : function(q){
12167         var p = {};
12168         //p[this.queryParam] = q;
12169         
12170         if(this.pageSize){
12171             p.start = 0;
12172             p.limit = this.pageSize;
12173         }
12174         return p;
12175     },
12176
12177     /**
12178      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12179      */
12180     collapse : function(){
12181         if(!this.isExpanded()){
12182             return;
12183         }
12184         
12185         this.list.hide();
12186         
12187         if(this.tickable){
12188             this.hasFocus = false;
12189             this.okBtn.hide();
12190             this.cancelBtn.hide();
12191             this.trigger.show();
12192             
12193             if(this.editable){
12194                 this.tickableInputEl().dom.value = '';
12195                 this.tickableInputEl().blur();
12196             }
12197             
12198         }
12199         
12200         Roo.get(document).un('mousedown', this.collapseIf, this);
12201         Roo.get(document).un('mousewheel', this.collapseIf, this);
12202         if (!this.editable) {
12203             Roo.get(document).un('keydown', this.listKeyPress, this);
12204         }
12205         this.fireEvent('collapse', this);
12206     },
12207
12208     // private
12209     collapseIf : function(e){
12210         var in_combo  = e.within(this.el);
12211         var in_list =  e.within(this.list);
12212         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12213         
12214         if (in_combo || in_list || is_list) {
12215             //e.stopPropagation();
12216             return;
12217         }
12218         
12219         if(this.tickable){
12220             this.onTickableFooterButtonClick(e, false, false);
12221         }
12222
12223         this.collapse();
12224         
12225     },
12226
12227     /**
12228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12229      */
12230     expand : function(){
12231        
12232         if(this.isExpanded() || !this.hasFocus){
12233             return;
12234         }
12235         
12236         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12237         this.list.setWidth(lw);
12238         
12239         
12240          Roo.log('expand');
12241         
12242         this.list.show();
12243         
12244         this.restrictHeight();
12245         
12246         if(this.tickable){
12247             
12248             this.tickItems = Roo.apply([], this.item);
12249             
12250             this.okBtn.show();
12251             this.cancelBtn.show();
12252             this.trigger.hide();
12253             
12254             if(this.editable){
12255                 this.tickableInputEl().focus();
12256             }
12257             
12258         }
12259         
12260         Roo.get(document).on('mousedown', this.collapseIf, this);
12261         Roo.get(document).on('mousewheel', this.collapseIf, this);
12262         if (!this.editable) {
12263             Roo.get(document).on('keydown', this.listKeyPress, this);
12264         }
12265         
12266         this.fireEvent('expand', this);
12267     },
12268
12269     // private
12270     // Implements the default empty TriggerField.onTriggerClick function
12271     onTriggerClick : function(e)
12272     {
12273         Roo.log('trigger click');
12274         
12275         if(this.disabled || !this.triggerList){
12276             return;
12277         }
12278         
12279         this.page = 0;
12280         this.loadNext = false;
12281         
12282         if(this.isExpanded()){
12283             this.collapse();
12284             if (!this.blockFocus) {
12285                 this.inputEl().focus();
12286             }
12287             
12288         }else {
12289             this.hasFocus = true;
12290             if(this.triggerAction == 'all') {
12291                 this.doQuery(this.allQuery, true);
12292             } else {
12293                 this.doQuery(this.getRawValue());
12294             }
12295             if (!this.blockFocus) {
12296                 this.inputEl().focus();
12297             }
12298         }
12299     },
12300     
12301     onTickableTriggerClick : function(e)
12302     {
12303         if(this.disabled){
12304             return;
12305         }
12306         
12307         this.page = 0;
12308         this.loadNext = false;
12309         this.hasFocus = true;
12310         
12311         if(this.triggerAction == 'all') {
12312             this.doQuery(this.allQuery, true);
12313         } else {
12314             this.doQuery(this.getRawValue());
12315         }
12316     },
12317     
12318     onSearchFieldClick : function(e)
12319     {
12320         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12321             this.onTickableFooterButtonClick(e, false, false);
12322             return;
12323         }
12324         
12325         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12326             return;
12327         }
12328         
12329         this.page = 0;
12330         this.loadNext = false;
12331         this.hasFocus = true;
12332         
12333         if(this.triggerAction == 'all') {
12334             this.doQuery(this.allQuery, true);
12335         } else {
12336             this.doQuery(this.getRawValue());
12337         }
12338     },
12339     
12340     listKeyPress : function(e)
12341     {
12342         //Roo.log('listkeypress');
12343         // scroll to first matching element based on key pres..
12344         if (e.isSpecialKey()) {
12345             return false;
12346         }
12347         var k = String.fromCharCode(e.getKey()).toUpperCase();
12348         //Roo.log(k);
12349         var match  = false;
12350         var csel = this.view.getSelectedNodes();
12351         var cselitem = false;
12352         if (csel.length) {
12353             var ix = this.view.indexOf(csel[0]);
12354             cselitem  = this.store.getAt(ix);
12355             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12356                 cselitem = false;
12357             }
12358             
12359         }
12360         
12361         this.store.each(function(v) { 
12362             if (cselitem) {
12363                 // start at existing selection.
12364                 if (cselitem.id == v.id) {
12365                     cselitem = false;
12366                 }
12367                 return true;
12368             }
12369                 
12370             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12371                 match = this.store.indexOf(v);
12372                 return false;
12373             }
12374             return true;
12375         }, this);
12376         
12377         if (match === false) {
12378             return true; // no more action?
12379         }
12380         // scroll to?
12381         this.view.select(match);
12382         var sn = Roo.get(this.view.getSelectedNodes()[0])
12383         sn.scrollIntoView(sn.dom.parentNode, false);
12384     },
12385     
12386     onViewScroll : function(e, t){
12387         
12388         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){
12389             return;
12390         }
12391         
12392         this.hasQuery = true;
12393         
12394         this.loading = this.list.select('.loading', true).first();
12395         
12396         if(this.loading === null){
12397             this.list.createChild({
12398                 tag: 'div',
12399                 cls: 'loading select2-more-results select2-active',
12400                 html: 'Loading more results...'
12401             })
12402             
12403             this.loading = this.list.select('.loading', true).first();
12404             
12405             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12406             
12407             this.loading.hide();
12408         }
12409         
12410         this.loading.show();
12411         
12412         var _combo = this;
12413         
12414         this.page++;
12415         this.loadNext = true;
12416         
12417         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12418         
12419         return;
12420     },
12421     
12422     addItem : function(o)
12423     {   
12424         var dv = ''; // display value
12425         
12426         if (this.displayField) {
12427             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12428         } else {
12429             // this is an error condition!!!
12430             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12431         }
12432         
12433         if(!dv.length){
12434             return;
12435         }
12436         
12437         var choice = this.choices.createChild({
12438             tag: 'li',
12439             cls: 'select2-search-choice',
12440             cn: [
12441                 {
12442                     tag: 'div',
12443                     html: dv
12444                 },
12445                 {
12446                     tag: 'a',
12447                     href: '#',
12448                     cls: 'select2-search-choice-close',
12449                     tabindex: '-1'
12450                 }
12451             ]
12452             
12453         }, this.searchField);
12454         
12455         var close = choice.select('a.select2-search-choice-close', true).first()
12456         
12457         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12458         
12459         this.item.push(o);
12460         
12461         this.lastData = o;
12462         
12463         this.syncValue();
12464         
12465         this.inputEl().dom.value = '';
12466         
12467         this.validate();
12468     },
12469     
12470     onRemoveItem : function(e, _self, o)
12471     {
12472         e.preventDefault();
12473         
12474         this.lastItem = Roo.apply([], this.item);
12475         
12476         var index = this.item.indexOf(o.data) * 1;
12477         
12478         if( index < 0){
12479             Roo.log('not this item?!');
12480             return;
12481         }
12482         
12483         this.item.splice(index, 1);
12484         o.item.remove();
12485         
12486         this.syncValue();
12487         
12488         this.fireEvent('remove', this, e);
12489         
12490         this.validate();
12491         
12492     },
12493     
12494     syncValue : function()
12495     {
12496         if(!this.item.length){
12497             this.clearValue();
12498             return;
12499         }
12500             
12501         var value = [];
12502         var _this = this;
12503         Roo.each(this.item, function(i){
12504             if(_this.valueField){
12505                 value.push(i[_this.valueField]);
12506                 return;
12507             }
12508
12509             value.push(i);
12510         });
12511
12512         this.value = value.join(',');
12513
12514         if(this.hiddenField){
12515             this.hiddenField.dom.value = this.value;
12516         }
12517         
12518         this.store.fireEvent("datachanged", this.store);
12519     },
12520     
12521     clearItem : function()
12522     {
12523         if(!this.multiple){
12524             return;
12525         }
12526         
12527         this.item = [];
12528         
12529         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12530            c.remove();
12531         });
12532         
12533         this.syncValue();
12534         
12535         this.validate();
12536     },
12537     
12538     inputEl: function ()
12539     {
12540         if(this.tickable){
12541             return this.searchField;
12542         }
12543         return this.el.select('input.form-control',true).first();
12544     },
12545     
12546     
12547     onTickableFooterButtonClick : function(e, btn, el)
12548     {
12549         e.preventDefault();
12550         
12551         this.lastItem = Roo.apply([], this.item);
12552         
12553         if(btn && btn.name == 'cancel'){
12554             this.tickItems = Roo.apply([], this.item);
12555             this.collapse();
12556             return;
12557         }
12558         
12559         this.clearItem();
12560         
12561         var _this = this;
12562         
12563         Roo.each(this.tickItems, function(o){
12564             _this.addItem(o);
12565         });
12566         
12567         this.collapse();
12568         
12569     },
12570     
12571     validate : function()
12572     {
12573         var v = this.getRawValue();
12574         
12575         if(this.multiple){
12576             v = this.getValue();
12577         }
12578         
12579         if(this.disabled || this.allowBlank || v.length){
12580             this.markValid();
12581             return true;
12582         }
12583         
12584         this.markInvalid();
12585         return false;
12586     },
12587     
12588     tickableInputEl : function()
12589     {
12590         if(!this.tickable || !this.editable){
12591             return this.inputEl();
12592         }
12593         
12594         return this.inputEl().select('.select2-search-field-input', true).first();
12595     }
12596     
12597     
12598
12599     /** 
12600     * @cfg {Boolean} grow 
12601     * @hide 
12602     */
12603     /** 
12604     * @cfg {Number} growMin 
12605     * @hide 
12606     */
12607     /** 
12608     * @cfg {Number} growMax 
12609     * @hide 
12610     */
12611     /**
12612      * @hide
12613      * @method autoSize
12614      */
12615 });
12616 /*
12617  * Based on:
12618  * Ext JS Library 1.1.1
12619  * Copyright(c) 2006-2007, Ext JS, LLC.
12620  *
12621  * Originally Released Under LGPL - original licence link has changed is not relivant.
12622  *
12623  * Fork - LGPL
12624  * <script type="text/javascript">
12625  */
12626
12627 /**
12628  * @class Roo.View
12629  * @extends Roo.util.Observable
12630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12631  * This class also supports single and multi selection modes. <br>
12632  * Create a data model bound view:
12633  <pre><code>
12634  var store = new Roo.data.Store(...);
12635
12636  var view = new Roo.View({
12637     el : "my-element",
12638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12639  
12640     singleSelect: true,
12641     selectedClass: "ydataview-selected",
12642     store: store
12643  });
12644
12645  // listen for node click?
12646  view.on("click", function(vw, index, node, e){
12647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12648  });
12649
12650  // load XML data
12651  dataModel.load("foobar.xml");
12652  </code></pre>
12653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12654  * <br><br>
12655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12657  * 
12658  * Note: old style constructor is still suported (container, template, config)
12659  * 
12660  * @constructor
12661  * Create a new View
12662  * @param {Object} config The config object
12663  * 
12664  */
12665 Roo.View = function(config, depreciated_tpl, depreciated_config){
12666     
12667     this.parent = false;
12668     
12669     if (typeof(depreciated_tpl) == 'undefined') {
12670         // new way.. - universal constructor.
12671         Roo.apply(this, config);
12672         this.el  = Roo.get(this.el);
12673     } else {
12674         // old format..
12675         this.el  = Roo.get(config);
12676         this.tpl = depreciated_tpl;
12677         Roo.apply(this, depreciated_config);
12678     }
12679     this.wrapEl  = this.el.wrap().wrap();
12680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12681     
12682     
12683     if(typeof(this.tpl) == "string"){
12684         this.tpl = new Roo.Template(this.tpl);
12685     } else {
12686         // support xtype ctors..
12687         this.tpl = new Roo.factory(this.tpl, Roo);
12688     }
12689     
12690     
12691     this.tpl.compile();
12692     
12693     /** @private */
12694     this.addEvents({
12695         /**
12696          * @event beforeclick
12697          * Fires before a click is processed. Returns false to cancel the default action.
12698          * @param {Roo.View} this
12699          * @param {Number} index The index of the target node
12700          * @param {HTMLElement} node The target node
12701          * @param {Roo.EventObject} e The raw event object
12702          */
12703             "beforeclick" : true,
12704         /**
12705          * @event click
12706          * Fires when a template node is clicked.
12707          * @param {Roo.View} this
12708          * @param {Number} index The index of the target node
12709          * @param {HTMLElement} node The target node
12710          * @param {Roo.EventObject} e The raw event object
12711          */
12712             "click" : true,
12713         /**
12714          * @event dblclick
12715          * Fires when a template node is double clicked.
12716          * @param {Roo.View} this
12717          * @param {Number} index The index of the target node
12718          * @param {HTMLElement} node The target node
12719          * @param {Roo.EventObject} e The raw event object
12720          */
12721             "dblclick" : true,
12722         /**
12723          * @event contextmenu
12724          * Fires when a template node is right clicked.
12725          * @param {Roo.View} this
12726          * @param {Number} index The index of the target node
12727          * @param {HTMLElement} node The target node
12728          * @param {Roo.EventObject} e The raw event object
12729          */
12730             "contextmenu" : true,
12731         /**
12732          * @event selectionchange
12733          * Fires when the selected nodes change.
12734          * @param {Roo.View} this
12735          * @param {Array} selections Array of the selected nodes
12736          */
12737             "selectionchange" : true,
12738     
12739         /**
12740          * @event beforeselect
12741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12742          * @param {Roo.View} this
12743          * @param {HTMLElement} node The node to be selected
12744          * @param {Array} selections Array of currently selected nodes
12745          */
12746             "beforeselect" : true,
12747         /**
12748          * @event preparedata
12749          * Fires on every row to render, to allow you to change the data.
12750          * @param {Roo.View} this
12751          * @param {Object} data to be rendered (change this)
12752          */
12753           "preparedata" : true
12754           
12755           
12756         });
12757
12758
12759
12760     this.el.on({
12761         "click": this.onClick,
12762         "dblclick": this.onDblClick,
12763         "contextmenu": this.onContextMenu,
12764         scope:this
12765     });
12766
12767     this.selections = [];
12768     this.nodes = [];
12769     this.cmp = new Roo.CompositeElementLite([]);
12770     if(this.store){
12771         this.store = Roo.factory(this.store, Roo.data);
12772         this.setStore(this.store, true);
12773     }
12774     
12775     if ( this.footer && this.footer.xtype) {
12776            
12777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12778         
12779         this.footer.dataSource = this.store
12780         this.footer.container = fctr;
12781         this.footer = Roo.factory(this.footer, Roo);
12782         fctr.insertFirst(this.el);
12783         
12784         // this is a bit insane - as the paging toolbar seems to detach the el..
12785 //        dom.parentNode.parentNode.parentNode
12786          // they get detached?
12787     }
12788     
12789     
12790     Roo.View.superclass.constructor.call(this);
12791     
12792     
12793 };
12794
12795 Roo.extend(Roo.View, Roo.util.Observable, {
12796     
12797      /**
12798      * @cfg {Roo.data.Store} store Data store to load data from.
12799      */
12800     store : false,
12801     
12802     /**
12803      * @cfg {String|Roo.Element} el The container element.
12804      */
12805     el : '',
12806     
12807     /**
12808      * @cfg {String|Roo.Template} tpl The template used by this View 
12809      */
12810     tpl : false,
12811     /**
12812      * @cfg {String} dataName the named area of the template to use as the data area
12813      *                          Works with domtemplates roo-name="name"
12814      */
12815     dataName: false,
12816     /**
12817      * @cfg {String} selectedClass The css class to add to selected nodes
12818      */
12819     selectedClass : "x-view-selected",
12820      /**
12821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12822      */
12823     emptyText : "",
12824     
12825     /**
12826      * @cfg {String} text to display on mask (default Loading)
12827      */
12828     mask : false,
12829     /**
12830      * @cfg {Boolean} multiSelect Allow multiple selection
12831      */
12832     multiSelect : false,
12833     /**
12834      * @cfg {Boolean} singleSelect Allow single selection
12835      */
12836     singleSelect:  false,
12837     
12838     /**
12839      * @cfg {Boolean} toggleSelect - selecting 
12840      */
12841     toggleSelect : false,
12842     
12843     /**
12844      * @cfg {Boolean} tickable - selecting 
12845      */
12846     tickable : false,
12847     
12848     /**
12849      * Returns the element this view is bound to.
12850      * @return {Roo.Element}
12851      */
12852     getEl : function(){
12853         return this.wrapEl;
12854     },
12855     
12856     
12857
12858     /**
12859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12860      */
12861     refresh : function(){
12862         //Roo.log('refresh');
12863         var t = this.tpl;
12864         
12865         // if we are using something like 'domtemplate', then
12866         // the what gets used is:
12867         // t.applySubtemplate(NAME, data, wrapping data..)
12868         // the outer template then get' applied with
12869         //     the store 'extra data'
12870         // and the body get's added to the
12871         //      roo-name="data" node?
12872         //      <span class='roo-tpl-{name}'></span> ?????
12873         
12874         
12875         
12876         this.clearSelections();
12877         this.el.update("");
12878         var html = [];
12879         var records = this.store.getRange();
12880         if(records.length < 1) {
12881             
12882             // is this valid??  = should it render a template??
12883             
12884             this.el.update(this.emptyText);
12885             return;
12886         }
12887         var el = this.el;
12888         if (this.dataName) {
12889             this.el.update(t.apply(this.store.meta)); //????
12890             el = this.el.child('.roo-tpl-' + this.dataName);
12891         }
12892         
12893         for(var i = 0, len = records.length; i < len; i++){
12894             var data = this.prepareData(records[i].data, i, records[i]);
12895             this.fireEvent("preparedata", this, data, i, records[i]);
12896             
12897             var d = Roo.apply({}, data);
12898             
12899             if(this.tickable){
12900                 Roo.apply(d, {'roo-id' : Roo.id()});
12901                 
12902                 var _this = this;
12903             
12904                 Roo.each(this.parent.item, function(item){
12905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12906                         return;
12907                     }
12908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12909                 });
12910             }
12911             
12912             html[html.length] = Roo.util.Format.trim(
12913                 this.dataName ?
12914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12915                     t.apply(d)
12916             );
12917         }
12918         
12919         
12920         
12921         el.update(html.join(""));
12922         this.nodes = el.dom.childNodes;
12923         this.updateIndexes(0);
12924     },
12925     
12926
12927     /**
12928      * Function to override to reformat the data that is sent to
12929      * the template for each node.
12930      * DEPRICATED - use the preparedata event handler.
12931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12932      * a JSON object for an UpdateManager bound view).
12933      */
12934     prepareData : function(data, index, record)
12935     {
12936         this.fireEvent("preparedata", this, data, index, record);
12937         return data;
12938     },
12939
12940     onUpdate : function(ds, record){
12941         // Roo.log('on update');   
12942         this.clearSelections();
12943         var index = this.store.indexOf(record);
12944         var n = this.nodes[index];
12945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12946         n.parentNode.removeChild(n);
12947         this.updateIndexes(index, index);
12948     },
12949
12950     
12951     
12952 // --------- FIXME     
12953     onAdd : function(ds, records, index)
12954     {
12955         //Roo.log(['on Add', ds, records, index] );        
12956         this.clearSelections();
12957         if(this.nodes.length == 0){
12958             this.refresh();
12959             return;
12960         }
12961         var n = this.nodes[index];
12962         for(var i = 0, len = records.length; i < len; i++){
12963             var d = this.prepareData(records[i].data, i, records[i]);
12964             if(n){
12965                 this.tpl.insertBefore(n, d);
12966             }else{
12967                 
12968                 this.tpl.append(this.el, d);
12969             }
12970         }
12971         this.updateIndexes(index);
12972     },
12973
12974     onRemove : function(ds, record, index){
12975        // Roo.log('onRemove');
12976         this.clearSelections();
12977         var el = this.dataName  ?
12978             this.el.child('.roo-tpl-' + this.dataName) :
12979             this.el; 
12980         
12981         el.dom.removeChild(this.nodes[index]);
12982         this.updateIndexes(index);
12983     },
12984
12985     /**
12986      * Refresh an individual node.
12987      * @param {Number} index
12988      */
12989     refreshNode : function(index){
12990         this.onUpdate(this.store, this.store.getAt(index));
12991     },
12992
12993     updateIndexes : function(startIndex, endIndex){
12994         var ns = this.nodes;
12995         startIndex = startIndex || 0;
12996         endIndex = endIndex || ns.length - 1;
12997         for(var i = startIndex; i <= endIndex; i++){
12998             ns[i].nodeIndex = i;
12999         }
13000     },
13001
13002     /**
13003      * Changes the data store this view uses and refresh the view.
13004      * @param {Store} store
13005      */
13006     setStore : function(store, initial){
13007         if(!initial && this.store){
13008             this.store.un("datachanged", this.refresh);
13009             this.store.un("add", this.onAdd);
13010             this.store.un("remove", this.onRemove);
13011             this.store.un("update", this.onUpdate);
13012             this.store.un("clear", this.refresh);
13013             this.store.un("beforeload", this.onBeforeLoad);
13014             this.store.un("load", this.onLoad);
13015             this.store.un("loadexception", this.onLoad);
13016         }
13017         if(store){
13018           
13019             store.on("datachanged", this.refresh, this);
13020             store.on("add", this.onAdd, this);
13021             store.on("remove", this.onRemove, this);
13022             store.on("update", this.onUpdate, this);
13023             store.on("clear", this.refresh, this);
13024             store.on("beforeload", this.onBeforeLoad, this);
13025             store.on("load", this.onLoad, this);
13026             store.on("loadexception", this.onLoad, this);
13027         }
13028         
13029         if(store){
13030             this.refresh();
13031         }
13032     },
13033     /**
13034      * onbeforeLoad - masks the loading area.
13035      *
13036      */
13037     onBeforeLoad : function(store,opts)
13038     {
13039          //Roo.log('onBeforeLoad');   
13040         if (!opts.add) {
13041             this.el.update("");
13042         }
13043         this.el.mask(this.mask ? this.mask : "Loading" ); 
13044     },
13045     onLoad : function ()
13046     {
13047         this.el.unmask();
13048     },
13049     
13050
13051     /**
13052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13053      * @param {HTMLElement} node
13054      * @return {HTMLElement} The template node
13055      */
13056     findItemFromChild : function(node){
13057         var el = this.dataName  ?
13058             this.el.child('.roo-tpl-' + this.dataName,true) :
13059             this.el.dom; 
13060         
13061         if(!node || node.parentNode == el){
13062                     return node;
13063             }
13064             var p = node.parentNode;
13065             while(p && p != el){
13066             if(p.parentNode == el){
13067                 return p;
13068             }
13069             p = p.parentNode;
13070         }
13071             return null;
13072     },
13073
13074     /** @ignore */
13075     onClick : function(e){
13076         var item = this.findItemFromChild(e.getTarget());
13077         if(item){
13078             var index = this.indexOf(item);
13079             if(this.onItemClick(item, index, e) !== false){
13080                 this.fireEvent("click", this, index, item, e);
13081             }
13082         }else{
13083             this.clearSelections();
13084         }
13085     },
13086
13087     /** @ignore */
13088     onContextMenu : function(e){
13089         var item = this.findItemFromChild(e.getTarget());
13090         if(item){
13091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13092         }
13093     },
13094
13095     /** @ignore */
13096     onDblClick : function(e){
13097         var item = this.findItemFromChild(e.getTarget());
13098         if(item){
13099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13100         }
13101     },
13102
13103     onItemClick : function(item, index, e)
13104     {
13105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13106             return false;
13107         }
13108         if (this.toggleSelect) {
13109             var m = this.isSelected(item) ? 'unselect' : 'select';
13110             //Roo.log(m);
13111             var _t = this;
13112             _t[m](item, true, false);
13113             return true;
13114         }
13115         if(this.multiSelect || this.singleSelect){
13116             if(this.multiSelect && e.shiftKey && this.lastSelection){
13117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13118             }else{
13119                 this.select(item, this.multiSelect && e.ctrlKey);
13120                 this.lastSelection = item;
13121             }
13122             
13123             if(!this.tickable){
13124                 e.preventDefault();
13125             }
13126             
13127         }
13128         return true;
13129     },
13130
13131     /**
13132      * Get the number of selected nodes.
13133      * @return {Number}
13134      */
13135     getSelectionCount : function(){
13136         return this.selections.length;
13137     },
13138
13139     /**
13140      * Get the currently selected nodes.
13141      * @return {Array} An array of HTMLElements
13142      */
13143     getSelectedNodes : function(){
13144         return this.selections;
13145     },
13146
13147     /**
13148      * Get the indexes of the selected nodes.
13149      * @return {Array}
13150      */
13151     getSelectedIndexes : function(){
13152         var indexes = [], s = this.selections;
13153         for(var i = 0, len = s.length; i < len; i++){
13154             indexes.push(s[i].nodeIndex);
13155         }
13156         return indexes;
13157     },
13158
13159     /**
13160      * Clear all selections
13161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13162      */
13163     clearSelections : function(suppressEvent){
13164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13165             this.cmp.elements = this.selections;
13166             this.cmp.removeClass(this.selectedClass);
13167             this.selections = [];
13168             if(!suppressEvent){
13169                 this.fireEvent("selectionchange", this, this.selections);
13170             }
13171         }
13172     },
13173
13174     /**
13175      * Returns true if the passed node is selected
13176      * @param {HTMLElement/Number} node The node or node index
13177      * @return {Boolean}
13178      */
13179     isSelected : function(node){
13180         var s = this.selections;
13181         if(s.length < 1){
13182             return false;
13183         }
13184         node = this.getNode(node);
13185         return s.indexOf(node) !== -1;
13186     },
13187
13188     /**
13189      * Selects nodes.
13190      * @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
13191      * @param {Boolean} keepExisting (optional) true to keep existing selections
13192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13193      */
13194     select : function(nodeInfo, keepExisting, suppressEvent){
13195         if(nodeInfo instanceof Array){
13196             if(!keepExisting){
13197                 this.clearSelections(true);
13198             }
13199             for(var i = 0, len = nodeInfo.length; i < len; i++){
13200                 this.select(nodeInfo[i], true, true);
13201             }
13202             return;
13203         } 
13204         var node = this.getNode(nodeInfo);
13205         if(!node || this.isSelected(node)){
13206             return; // already selected.
13207         }
13208         if(!keepExisting){
13209             this.clearSelections(true);
13210         }
13211         
13212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13213             Roo.fly(node).addClass(this.selectedClass);
13214             this.selections.push(node);
13215             if(!suppressEvent){
13216                 this.fireEvent("selectionchange", this, this.selections);
13217             }
13218         }
13219         
13220         
13221     },
13222       /**
13223      * Unselects nodes.
13224      * @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
13225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13227      */
13228     unselect : function(nodeInfo, keepExisting, suppressEvent)
13229     {
13230         if(nodeInfo instanceof Array){
13231             Roo.each(this.selections, function(s) {
13232                 this.unselect(s, nodeInfo);
13233             }, this);
13234             return;
13235         }
13236         var node = this.getNode(nodeInfo);
13237         if(!node || !this.isSelected(node)){
13238             //Roo.log("not selected");
13239             return; // not selected.
13240         }
13241         // fireevent???
13242         var ns = [];
13243         Roo.each(this.selections, function(s) {
13244             if (s == node ) {
13245                 Roo.fly(node).removeClass(this.selectedClass);
13246
13247                 return;
13248             }
13249             ns.push(s);
13250         },this);
13251         
13252         this.selections= ns;
13253         this.fireEvent("selectionchange", this, this.selections);
13254     },
13255
13256     /**
13257      * Gets a template node.
13258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13259      * @return {HTMLElement} The node or null if it wasn't found
13260      */
13261     getNode : function(nodeInfo){
13262         if(typeof nodeInfo == "string"){
13263             return document.getElementById(nodeInfo);
13264         }else if(typeof nodeInfo == "number"){
13265             return this.nodes[nodeInfo];
13266         }
13267         return nodeInfo;
13268     },
13269
13270     /**
13271      * Gets a range template nodes.
13272      * @param {Number} startIndex
13273      * @param {Number} endIndex
13274      * @return {Array} An array of nodes
13275      */
13276     getNodes : function(start, end){
13277         var ns = this.nodes;
13278         start = start || 0;
13279         end = typeof end == "undefined" ? ns.length - 1 : end;
13280         var nodes = [];
13281         if(start <= end){
13282             for(var i = start; i <= end; i++){
13283                 nodes.push(ns[i]);
13284             }
13285         } else{
13286             for(var i = start; i >= end; i--){
13287                 nodes.push(ns[i]);
13288             }
13289         }
13290         return nodes;
13291     },
13292
13293     /**
13294      * Finds the index of the passed node
13295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13296      * @return {Number} The index of the node or -1
13297      */
13298     indexOf : function(node){
13299         node = this.getNode(node);
13300         if(typeof node.nodeIndex == "number"){
13301             return node.nodeIndex;
13302         }
13303         var ns = this.nodes;
13304         for(var i = 0, len = ns.length; i < len; i++){
13305             if(ns[i] == node){
13306                 return i;
13307             }
13308         }
13309         return -1;
13310     }
13311 });
13312 /*
13313  * - LGPL
13314  *
13315  * based on jquery fullcalendar
13316  * 
13317  */
13318
13319 Roo.bootstrap = Roo.bootstrap || {};
13320 /**
13321  * @class Roo.bootstrap.Calendar
13322  * @extends Roo.bootstrap.Component
13323  * Bootstrap Calendar class
13324  * @cfg {Boolean} loadMask (true|false) default false
13325  * @cfg {Object} header generate the user specific header of the calendar, default false
13326
13327  * @constructor
13328  * Create a new Container
13329  * @param {Object} config The config object
13330  */
13331
13332
13333
13334 Roo.bootstrap.Calendar = function(config){
13335     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13336      this.addEvents({
13337         /**
13338              * @event select
13339              * Fires when a date is selected
13340              * @param {DatePicker} this
13341              * @param {Date} date The selected date
13342              */
13343         'select': true,
13344         /**
13345              * @event monthchange
13346              * Fires when the displayed month changes 
13347              * @param {DatePicker} this
13348              * @param {Date} date The selected month
13349              */
13350         'monthchange': true,
13351         /**
13352              * @event evententer
13353              * Fires when mouse over an event
13354              * @param {Calendar} this
13355              * @param {event} Event
13356              */
13357         'evententer': true,
13358         /**
13359              * @event eventleave
13360              * Fires when the mouse leaves an
13361              * @param {Calendar} this
13362              * @param {event}
13363              */
13364         'eventleave': true,
13365         /**
13366              * @event eventclick
13367              * Fires when the mouse click an
13368              * @param {Calendar} this
13369              * @param {event}
13370              */
13371         'eventclick': true
13372         
13373     });
13374
13375 };
13376
13377 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13378     
13379      /**
13380      * @cfg {Number} startDay
13381      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13382      */
13383     startDay : 0,
13384     
13385     loadMask : false,
13386     
13387     header : false,
13388       
13389     getAutoCreate : function(){
13390         
13391         
13392         var fc_button = function(name, corner, style, content ) {
13393             return Roo.apply({},{
13394                 tag : 'span',
13395                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13396                          (corner.length ?
13397                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13398                             ''
13399                         ),
13400                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13401                 unselectable: 'on'
13402             });
13403         };
13404         
13405         var header = {};
13406         
13407         if(!this.header){
13408             header = {
13409                 tag : 'table',
13410                 cls : 'fc-header',
13411                 style : 'width:100%',
13412                 cn : [
13413                     {
13414                         tag: 'tr',
13415                         cn : [
13416                             {
13417                                 tag : 'td',
13418                                 cls : 'fc-header-left',
13419                                 cn : [
13420                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13421                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13422                                     { tag: 'span', cls: 'fc-header-space' },
13423                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13424
13425
13426                                 ]
13427                             },
13428
13429                             {
13430                                 tag : 'td',
13431                                 cls : 'fc-header-center',
13432                                 cn : [
13433                                     {
13434                                         tag: 'span',
13435                                         cls: 'fc-header-title',
13436                                         cn : {
13437                                             tag: 'H2',
13438                                             html : 'month / year'
13439                                         }
13440                                     }
13441
13442                                 ]
13443                             },
13444                             {
13445                                 tag : 'td',
13446                                 cls : 'fc-header-right',
13447                                 cn : [
13448                               /*      fc_button('month', 'left', '', 'month' ),
13449                                     fc_button('week', '', '', 'week' ),
13450                                     fc_button('day', 'right', '', 'day' )
13451                                 */    
13452
13453                                 ]
13454                             }
13455
13456                         ]
13457                     }
13458                 ]
13459             };
13460         }
13461         
13462         header = this.header;
13463         
13464        
13465         var cal_heads = function() {
13466             var ret = [];
13467             // fixme - handle this.
13468             
13469             for (var i =0; i < Date.dayNames.length; i++) {
13470                 var d = Date.dayNames[i];
13471                 ret.push({
13472                     tag: 'th',
13473                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13474                     html : d.substring(0,3)
13475                 });
13476                 
13477             }
13478             ret[0].cls += ' fc-first';
13479             ret[6].cls += ' fc-last';
13480             return ret;
13481         };
13482         var cal_cell = function(n) {
13483             return  {
13484                 tag: 'td',
13485                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13486                 cn : [
13487                     {
13488                         cn : [
13489                             {
13490                                 cls: 'fc-day-number',
13491                                 html: 'D'
13492                             },
13493                             {
13494                                 cls: 'fc-day-content',
13495                              
13496                                 cn : [
13497                                      {
13498                                         style: 'position: relative;' // height: 17px;
13499                                     }
13500                                 ]
13501                             }
13502                             
13503                             
13504                         ]
13505                     }
13506                 ]
13507                 
13508             }
13509         };
13510         var cal_rows = function() {
13511             
13512             var ret = [];
13513             for (var r = 0; r < 6; r++) {
13514                 var row= {
13515                     tag : 'tr',
13516                     cls : 'fc-week',
13517                     cn : []
13518                 };
13519                 
13520                 for (var i =0; i < Date.dayNames.length; i++) {
13521                     var d = Date.dayNames[i];
13522                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13523
13524                 }
13525                 row.cn[0].cls+=' fc-first';
13526                 row.cn[0].cn[0].style = 'min-height:90px';
13527                 row.cn[6].cls+=' fc-last';
13528                 ret.push(row);
13529                 
13530             }
13531             ret[0].cls += ' fc-first';
13532             ret[4].cls += ' fc-prev-last';
13533             ret[5].cls += ' fc-last';
13534             return ret;
13535             
13536         };
13537         
13538         var cal_table = {
13539             tag: 'table',
13540             cls: 'fc-border-separate',
13541             style : 'width:100%',
13542             cellspacing  : 0,
13543             cn : [
13544                 { 
13545                     tag: 'thead',
13546                     cn : [
13547                         { 
13548                             tag: 'tr',
13549                             cls : 'fc-first fc-last',
13550                             cn : cal_heads()
13551                         }
13552                     ]
13553                 },
13554                 { 
13555                     tag: 'tbody',
13556                     cn : cal_rows()
13557                 }
13558                   
13559             ]
13560         };
13561          
13562          var cfg = {
13563             cls : 'fc fc-ltr',
13564             cn : [
13565                 header,
13566                 {
13567                     cls : 'fc-content',
13568                     style : "position: relative;",
13569                     cn : [
13570                         {
13571                             cls : 'fc-view fc-view-month fc-grid',
13572                             style : 'position: relative',
13573                             unselectable : 'on',
13574                             cn : [
13575                                 {
13576                                     cls : 'fc-event-container',
13577                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13578                                 },
13579                                 cal_table
13580                             ]
13581                         }
13582                     ]
13583     
13584                 }
13585            ] 
13586             
13587         };
13588         
13589          
13590         
13591         return cfg;
13592     },
13593     
13594     
13595     initEvents : function()
13596     {
13597         if(!this.store){
13598             throw "can not find store for calendar";
13599         }
13600         
13601         var mark = {
13602             tag: "div",
13603             cls:"x-dlg-mask",
13604             style: "text-align:center",
13605             cn: [
13606                 {
13607                     tag: "div",
13608                     style: "background-color:white;width:50%;margin:250 auto",
13609                     cn: [
13610                         {
13611                             tag: "img",
13612                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13613                         },
13614                         {
13615                             tag: "span",
13616                             html: "Loading"
13617                         }
13618                         
13619                     ]
13620                 }
13621             ]
13622         }
13623         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13624         
13625         var size = this.el.select('.fc-content', true).first().getSize();
13626         this.maskEl.setSize(size.width, size.height);
13627         this.maskEl.enableDisplayMode("block");
13628         if(!this.loadMask){
13629             this.maskEl.hide();
13630         }
13631         
13632         this.store = Roo.factory(this.store, Roo.data);
13633         this.store.on('load', this.onLoad, this);
13634         this.store.on('beforeload', this.onBeforeLoad, this);
13635         
13636         this.resize();
13637         
13638         this.cells = this.el.select('.fc-day',true);
13639         //Roo.log(this.cells);
13640         this.textNodes = this.el.query('.fc-day-number');
13641         this.cells.addClassOnOver('fc-state-hover');
13642         
13643         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13644         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13645         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13646         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13647         
13648         this.on('monthchange', this.onMonthChange, this);
13649         
13650         this.update(new Date().clearTime());
13651     },
13652     
13653     resize : function() {
13654         var sz  = this.el.getSize();
13655         
13656         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13657         this.el.select('.fc-day-content div',true).setHeight(34);
13658     },
13659     
13660     
13661     // private
13662     showPrevMonth : function(e){
13663         this.update(this.activeDate.add("mo", -1));
13664     },
13665     showToday : function(e){
13666         this.update(new Date().clearTime());
13667     },
13668     // private
13669     showNextMonth : function(e){
13670         this.update(this.activeDate.add("mo", 1));
13671     },
13672
13673     // private
13674     showPrevYear : function(){
13675         this.update(this.activeDate.add("y", -1));
13676     },
13677
13678     // private
13679     showNextYear : function(){
13680         this.update(this.activeDate.add("y", 1));
13681     },
13682
13683     
13684    // private
13685     update : function(date)
13686     {
13687         var vd = this.activeDate;
13688         this.activeDate = date;
13689 //        if(vd && this.el){
13690 //            var t = date.getTime();
13691 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13692 //                Roo.log('using add remove');
13693 //                
13694 //                this.fireEvent('monthchange', this, date);
13695 //                
13696 //                this.cells.removeClass("fc-state-highlight");
13697 //                this.cells.each(function(c){
13698 //                   if(c.dateValue == t){
13699 //                       c.addClass("fc-state-highlight");
13700 //                       setTimeout(function(){
13701 //                            try{c.dom.firstChild.focus();}catch(e){}
13702 //                       }, 50);
13703 //                       return false;
13704 //                   }
13705 //                   return true;
13706 //                });
13707 //                return;
13708 //            }
13709 //        }
13710         
13711         var days = date.getDaysInMonth();
13712         
13713         var firstOfMonth = date.getFirstDateOfMonth();
13714         var startingPos = firstOfMonth.getDay()-this.startDay;
13715         
13716         if(startingPos < this.startDay){
13717             startingPos += 7;
13718         }
13719         
13720         var pm = date.add(Date.MONTH, -1);
13721         var prevStart = pm.getDaysInMonth()-startingPos;
13722 //        
13723         this.cells = this.el.select('.fc-day',true);
13724         this.textNodes = this.el.query('.fc-day-number');
13725         this.cells.addClassOnOver('fc-state-hover');
13726         
13727         var cells = this.cells.elements;
13728         var textEls = this.textNodes;
13729         
13730         Roo.each(cells, function(cell){
13731             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13732         });
13733         
13734         days += startingPos;
13735
13736         // convert everything to numbers so it's fast
13737         var day = 86400000;
13738         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13739         //Roo.log(d);
13740         //Roo.log(pm);
13741         //Roo.log(prevStart);
13742         
13743         var today = new Date().clearTime().getTime();
13744         var sel = date.clearTime().getTime();
13745         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13746         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13747         var ddMatch = this.disabledDatesRE;
13748         var ddText = this.disabledDatesText;
13749         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13750         var ddaysText = this.disabledDaysText;
13751         var format = this.format;
13752         
13753         var setCellClass = function(cal, cell){
13754             cell.row = 0;
13755             cell.events = [];
13756             cell.more = [];
13757             //Roo.log('set Cell Class');
13758             cell.title = "";
13759             var t = d.getTime();
13760             
13761             //Roo.log(d);
13762             
13763             cell.dateValue = t;
13764             if(t == today){
13765                 cell.className += " fc-today";
13766                 cell.className += " fc-state-highlight";
13767                 cell.title = cal.todayText;
13768             }
13769             if(t == sel){
13770                 // disable highlight in other month..
13771                 //cell.className += " fc-state-highlight";
13772                 
13773             }
13774             // disabling
13775             if(t < min) {
13776                 cell.className = " fc-state-disabled";
13777                 cell.title = cal.minText;
13778                 return;
13779             }
13780             if(t > max) {
13781                 cell.className = " fc-state-disabled";
13782                 cell.title = cal.maxText;
13783                 return;
13784             }
13785             if(ddays){
13786                 if(ddays.indexOf(d.getDay()) != -1){
13787                     cell.title = ddaysText;
13788                     cell.className = " fc-state-disabled";
13789                 }
13790             }
13791             if(ddMatch && format){
13792                 var fvalue = d.dateFormat(format);
13793                 if(ddMatch.test(fvalue)){
13794                     cell.title = ddText.replace("%0", fvalue);
13795                     cell.className = " fc-state-disabled";
13796                 }
13797             }
13798             
13799             if (!cell.initialClassName) {
13800                 cell.initialClassName = cell.dom.className;
13801             }
13802             
13803             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13804         };
13805
13806         var i = 0;
13807         
13808         for(; i < startingPos; i++) {
13809             textEls[i].innerHTML = (++prevStart);
13810             d.setDate(d.getDate()+1);
13811             
13812             cells[i].className = "fc-past fc-other-month";
13813             setCellClass(this, cells[i]);
13814         }
13815         
13816         var intDay = 0;
13817         
13818         for(; i < days; i++){
13819             intDay = i - startingPos + 1;
13820             textEls[i].innerHTML = (intDay);
13821             d.setDate(d.getDate()+1);
13822             
13823             cells[i].className = ''; // "x-date-active";
13824             setCellClass(this, cells[i]);
13825         }
13826         var extraDays = 0;
13827         
13828         for(; i < 42; i++) {
13829             textEls[i].innerHTML = (++extraDays);
13830             d.setDate(d.getDate()+1);
13831             
13832             cells[i].className = "fc-future fc-other-month";
13833             setCellClass(this, cells[i]);
13834         }
13835         
13836         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13837         
13838         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13839         
13840         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13841         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13842         
13843         if(totalRows != 6){
13844             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13845             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13846         }
13847         
13848         this.fireEvent('monthchange', this, date);
13849         
13850         
13851         /*
13852         if(!this.internalRender){
13853             var main = this.el.dom.firstChild;
13854             var w = main.offsetWidth;
13855             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13856             Roo.fly(main).setWidth(w);
13857             this.internalRender = true;
13858             // opera does not respect the auto grow header center column
13859             // then, after it gets a width opera refuses to recalculate
13860             // without a second pass
13861             if(Roo.isOpera && !this.secondPass){
13862                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13863                 this.secondPass = true;
13864                 this.update.defer(10, this, [date]);
13865             }
13866         }
13867         */
13868         
13869     },
13870     
13871     findCell : function(dt) {
13872         dt = dt.clearTime().getTime();
13873         var ret = false;
13874         this.cells.each(function(c){
13875             //Roo.log("check " +c.dateValue + '?=' + dt);
13876             if(c.dateValue == dt){
13877                 ret = c;
13878                 return false;
13879             }
13880             return true;
13881         });
13882         
13883         return ret;
13884     },
13885     
13886     findCells : function(ev) {
13887         var s = ev.start.clone().clearTime().getTime();
13888        // Roo.log(s);
13889         var e= ev.end.clone().clearTime().getTime();
13890        // Roo.log(e);
13891         var ret = [];
13892         this.cells.each(function(c){
13893              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13894             
13895             if(c.dateValue > e){
13896                 return ;
13897             }
13898             if(c.dateValue < s){
13899                 return ;
13900             }
13901             ret.push(c);
13902         });
13903         
13904         return ret;    
13905     },
13906     
13907 //    findBestRow: function(cells)
13908 //    {
13909 //        var ret = 0;
13910 //        
13911 //        for (var i =0 ; i < cells.length;i++) {
13912 //            ret  = Math.max(cells[i].rows || 0,ret);
13913 //        }
13914 //        return ret;
13915 //        
13916 //    },
13917     
13918     
13919     addItem : function(ev)
13920     {
13921         // look for vertical location slot in
13922         var cells = this.findCells(ev);
13923         
13924 //        ev.row = this.findBestRow(cells);
13925         
13926         // work out the location.
13927         
13928         var crow = false;
13929         var rows = [];
13930         for(var i =0; i < cells.length; i++) {
13931             
13932             cells[i].row = cells[0].row;
13933             
13934             if(i == 0){
13935                 cells[i].row = cells[i].row + 1;
13936             }
13937             
13938             if (!crow) {
13939                 crow = {
13940                     start : cells[i],
13941                     end :  cells[i]
13942                 };
13943                 continue;
13944             }
13945             if (crow.start.getY() == cells[i].getY()) {
13946                 // on same row.
13947                 crow.end = cells[i];
13948                 continue;
13949             }
13950             // different row.
13951             rows.push(crow);
13952             crow = {
13953                 start: cells[i],
13954                 end : cells[i]
13955             };
13956             
13957         }
13958         
13959         rows.push(crow);
13960         ev.els = [];
13961         ev.rows = rows;
13962         ev.cells = cells;
13963         
13964         cells[0].events.push(ev);
13965         
13966         this.calevents.push(ev);
13967     },
13968     
13969     clearEvents: function() {
13970         
13971         if(!this.calevents){
13972             return;
13973         }
13974         
13975         Roo.each(this.cells.elements, function(c){
13976             c.row = 0;
13977             c.events = [];
13978             c.more = [];
13979         });
13980         
13981         Roo.each(this.calevents, function(e) {
13982             Roo.each(e.els, function(el) {
13983                 el.un('mouseenter' ,this.onEventEnter, this);
13984                 el.un('mouseleave' ,this.onEventLeave, this);
13985                 el.remove();
13986             },this);
13987         },this);
13988         
13989         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13990             e.remove();
13991         });
13992         
13993     },
13994     
13995     renderEvents: function()
13996     {   
13997         var _this = this;
13998         
13999         this.cells.each(function(c) {
14000             
14001             if(c.row < 5){
14002                 return;
14003             }
14004             
14005             var ev = c.events;
14006             
14007             var r = 4;
14008             if(c.row != c.events.length){
14009                 r = 4 - (4 - (c.row - c.events.length));
14010             }
14011             
14012             c.events = ev.slice(0, r);
14013             c.more = ev.slice(r);
14014             
14015             if(c.more.length && c.more.length == 1){
14016                 c.events.push(c.more.pop());
14017             }
14018             
14019             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14020             
14021         });
14022             
14023         this.cells.each(function(c) {
14024             
14025             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14026             
14027             
14028             for (var e = 0; e < c.events.length; e++){
14029                 var ev = c.events[e];
14030                 var rows = ev.rows;
14031                 
14032                 for(var i = 0; i < rows.length; i++) {
14033                 
14034                     // how many rows should it span..
14035
14036                     var  cfg = {
14037                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14038                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14039
14040                         unselectable : "on",
14041                         cn : [
14042                             {
14043                                 cls: 'fc-event-inner',
14044                                 cn : [
14045     //                                {
14046     //                                  tag:'span',
14047     //                                  cls: 'fc-event-time',
14048     //                                  html : cells.length > 1 ? '' : ev.time
14049     //                                },
14050                                     {
14051                                       tag:'span',
14052                                       cls: 'fc-event-title',
14053                                       html : String.format('{0}', ev.title)
14054                                     }
14055
14056
14057                                 ]
14058                             },
14059                             {
14060                                 cls: 'ui-resizable-handle ui-resizable-e',
14061                                 html : '&nbsp;&nbsp;&nbsp'
14062                             }
14063
14064                         ]
14065                     };
14066
14067                     if (i == 0) {
14068                         cfg.cls += ' fc-event-start';
14069                     }
14070                     if ((i+1) == rows.length) {
14071                         cfg.cls += ' fc-event-end';
14072                     }
14073
14074                     var ctr = _this.el.select('.fc-event-container',true).first();
14075                     var cg = ctr.createChild(cfg);
14076
14077                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14078                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14079
14080                     var r = (c.more.length) ? 1 : 0;
14081                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14082                     cg.setWidth(ebox.right - sbox.x -2);
14083
14084                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14085                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14086                     cg.on('click', _this.onEventClick, _this, ev);
14087
14088                     ev.els.push(cg);
14089                     
14090                 }
14091                 
14092             }
14093             
14094             
14095             if(c.more.length){
14096                 var  cfg = {
14097                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14098                     style : 'position: absolute',
14099                     unselectable : "on",
14100                     cn : [
14101                         {
14102                             cls: 'fc-event-inner',
14103                             cn : [
14104                                 {
14105                                   tag:'span',
14106                                   cls: 'fc-event-title',
14107                                   html : 'More'
14108                                 }
14109
14110
14111                             ]
14112                         },
14113                         {
14114                             cls: 'ui-resizable-handle ui-resizable-e',
14115                             html : '&nbsp;&nbsp;&nbsp'
14116                         }
14117
14118                     ]
14119                 };
14120
14121                 var ctr = _this.el.select('.fc-event-container',true).first();
14122                 var cg = ctr.createChild(cfg);
14123
14124                 var sbox = c.select('.fc-day-content',true).first().getBox();
14125                 var ebox = c.select('.fc-day-content',true).first().getBox();
14126                 //Roo.log(cg);
14127                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14128                 cg.setWidth(ebox.right - sbox.x -2);
14129
14130                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14131                 
14132             }
14133             
14134         });
14135         
14136         
14137         
14138     },
14139     
14140     onEventEnter: function (e, el,event,d) {
14141         this.fireEvent('evententer', this, el, event);
14142     },
14143     
14144     onEventLeave: function (e, el,event,d) {
14145         this.fireEvent('eventleave', this, el, event);
14146     },
14147     
14148     onEventClick: function (e, el,event,d) {
14149         this.fireEvent('eventclick', this, el, event);
14150     },
14151     
14152     onMonthChange: function () {
14153         this.store.load();
14154     },
14155     
14156     onMoreEventClick: function(e, el, more)
14157     {
14158         var _this = this;
14159         
14160         this.calpopover.placement = 'right';
14161         this.calpopover.setTitle('More');
14162         
14163         this.calpopover.setContent('');
14164         
14165         var ctr = this.calpopover.el.select('.popover-content', true).first();
14166         
14167         Roo.each(more, function(m){
14168             var cfg = {
14169                 cls : 'fc-event-hori fc-event-draggable',
14170                 html : m.title
14171             }
14172             var cg = ctr.createChild(cfg);
14173             
14174             cg.on('click', _this.onEventClick, _this, m);
14175         });
14176         
14177         this.calpopover.show(el);
14178         
14179         
14180     },
14181     
14182     onLoad: function () 
14183     {   
14184         this.calevents = [];
14185         var cal = this;
14186         
14187         if(this.store.getCount() > 0){
14188             this.store.data.each(function(d){
14189                cal.addItem({
14190                     id : d.data.id,
14191                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14192                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14193                     time : d.data.start_time,
14194                     title : d.data.title,
14195                     description : d.data.description,
14196                     venue : d.data.venue
14197                 });
14198             });
14199         }
14200         
14201         this.renderEvents();
14202         
14203         if(this.calevents.length && this.loadMask){
14204             this.maskEl.hide();
14205         }
14206     },
14207     
14208     onBeforeLoad: function()
14209     {
14210         this.clearEvents();
14211         if(this.loadMask){
14212             this.maskEl.show();
14213         }
14214     }
14215 });
14216
14217  
14218  /*
14219  * - LGPL
14220  *
14221  * element
14222  * 
14223  */
14224
14225 /**
14226  * @class Roo.bootstrap.Popover
14227  * @extends Roo.bootstrap.Component
14228  * Bootstrap Popover class
14229  * @cfg {String} html contents of the popover   (or false to use children..)
14230  * @cfg {String} title of popover (or false to hide)
14231  * @cfg {String} placement how it is placed
14232  * @cfg {String} trigger click || hover (or false to trigger manually)
14233  * @cfg {String} over what (parent or false to trigger manually.)
14234  * @cfg {Number} delay - delay before showing
14235  
14236  * @constructor
14237  * Create a new Popover
14238  * @param {Object} config The config object
14239  */
14240
14241 Roo.bootstrap.Popover = function(config){
14242     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14243 };
14244
14245 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14246     
14247     title: 'Fill in a title',
14248     html: false,
14249     
14250     placement : 'right',
14251     trigger : 'hover', // hover
14252     
14253     delay : 0,
14254     
14255     over: 'parent',
14256     
14257     can_build_overlaid : false,
14258     
14259     getChildContainer : function()
14260     {
14261         return this.el.select('.popover-content',true).first();
14262     },
14263     
14264     getAutoCreate : function(){
14265          Roo.log('make popover?');
14266         var cfg = {
14267            cls : 'popover roo-dynamic',
14268            style: 'display:block',
14269            cn : [
14270                 {
14271                     cls : 'arrow'
14272                 },
14273                 {
14274                     cls : 'popover-inner',
14275                     cn : [
14276                         {
14277                             tag: 'h3',
14278                             cls: 'popover-title',
14279                             html : this.title
14280                         },
14281                         {
14282                             cls : 'popover-content',
14283                             html : this.html
14284                         }
14285                     ]
14286                     
14287                 }
14288            ]
14289         };
14290         
14291         return cfg;
14292     },
14293     setTitle: function(str)
14294     {
14295         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14296     },
14297     setContent: function(str)
14298     {
14299         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14300     },
14301     // as it get's added to the bottom of the page.
14302     onRender : function(ct, position)
14303     {
14304         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14305         if(!this.el){
14306             var cfg = Roo.apply({},  this.getAutoCreate());
14307             cfg.id = Roo.id();
14308             
14309             if (this.cls) {
14310                 cfg.cls += ' ' + this.cls;
14311             }
14312             if (this.style) {
14313                 cfg.style = this.style;
14314             }
14315             Roo.log("adding to ")
14316             this.el = Roo.get(document.body).createChild(cfg, position);
14317             Roo.log(this.el);
14318         }
14319         this.initEvents();
14320     },
14321     
14322     initEvents : function()
14323     {
14324         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14325         this.el.enableDisplayMode('block');
14326         this.el.hide();
14327         if (this.over === false) {
14328             return; 
14329         }
14330         if (this.triggers === false) {
14331             return;
14332         }
14333         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14334         var triggers = this.trigger ? this.trigger.split(' ') : [];
14335         Roo.each(triggers, function(trigger) {
14336         
14337             if (trigger == 'click') {
14338                 on_el.on('click', this.toggle, this);
14339             } else if (trigger != 'manual') {
14340                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14341                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14342       
14343                 on_el.on(eventIn  ,this.enter, this);
14344                 on_el.on(eventOut, this.leave, this);
14345             }
14346         }, this);
14347         
14348     },
14349     
14350     
14351     // private
14352     timeout : null,
14353     hoverState : null,
14354     
14355     toggle : function () {
14356         this.hoverState == 'in' ? this.leave() : this.enter();
14357     },
14358     
14359     enter : function () {
14360        
14361     
14362         clearTimeout(this.timeout);
14363     
14364         this.hoverState = 'in';
14365     
14366         if (!this.delay || !this.delay.show) {
14367             this.show();
14368             return;
14369         }
14370         var _t = this;
14371         this.timeout = setTimeout(function () {
14372             if (_t.hoverState == 'in') {
14373                 _t.show();
14374             }
14375         }, this.delay.show)
14376     },
14377     leave : function() {
14378         clearTimeout(this.timeout);
14379     
14380         this.hoverState = 'out';
14381     
14382         if (!this.delay || !this.delay.hide) {
14383             this.hide();
14384             return;
14385         }
14386         var _t = this;
14387         this.timeout = setTimeout(function () {
14388             if (_t.hoverState == 'out') {
14389                 _t.hide();
14390             }
14391         }, this.delay.hide)
14392     },
14393     
14394     show : function (on_el)
14395     {
14396         if (!on_el) {
14397             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14398         }
14399         // set content.
14400         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14401         if (this.html !== false) {
14402             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14403         }
14404         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14405         if (!this.title.length) {
14406             this.el.select('.popover-title',true).hide();
14407         }
14408         
14409         var placement = typeof this.placement == 'function' ?
14410             this.placement.call(this, this.el, on_el) :
14411             this.placement;
14412             
14413         var autoToken = /\s?auto?\s?/i;
14414         var autoPlace = autoToken.test(placement);
14415         if (autoPlace) {
14416             placement = placement.replace(autoToken, '') || 'top';
14417         }
14418         
14419         //this.el.detach()
14420         //this.el.setXY([0,0]);
14421         this.el.show();
14422         this.el.dom.style.display='block';
14423         this.el.addClass(placement);
14424         
14425         //this.el.appendTo(on_el);
14426         
14427         var p = this.getPosition();
14428         var box = this.el.getBox();
14429         
14430         if (autoPlace) {
14431             // fixme..
14432         }
14433         var align = Roo.bootstrap.Popover.alignment[placement];
14434         this.el.alignTo(on_el, align[0],align[1]);
14435         //var arrow = this.el.select('.arrow',true).first();
14436         //arrow.set(align[2], 
14437         
14438         this.el.addClass('in');
14439         this.hoverState = null;
14440         
14441         if (this.el.hasClass('fade')) {
14442             // fade it?
14443         }
14444         
14445     },
14446     hide : function()
14447     {
14448         this.el.setXY([0,0]);
14449         this.el.removeClass('in');
14450         this.el.hide();
14451         
14452     }
14453     
14454 });
14455
14456 Roo.bootstrap.Popover.alignment = {
14457     'left' : ['r-l', [-10,0], 'right'],
14458     'right' : ['l-r', [10,0], 'left'],
14459     'bottom' : ['t-b', [0,10], 'top'],
14460     'top' : [ 'b-t', [0,-10], 'bottom']
14461 };
14462
14463  /*
14464  * - LGPL
14465  *
14466  * Progress
14467  * 
14468  */
14469
14470 /**
14471  * @class Roo.bootstrap.Progress
14472  * @extends Roo.bootstrap.Component
14473  * Bootstrap Progress class
14474  * @cfg {Boolean} striped striped of the progress bar
14475  * @cfg {Boolean} active animated of the progress bar
14476  * 
14477  * 
14478  * @constructor
14479  * Create a new Progress
14480  * @param {Object} config The config object
14481  */
14482
14483 Roo.bootstrap.Progress = function(config){
14484     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14485 };
14486
14487 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14488     
14489     striped : false,
14490     active: false,
14491     
14492     getAutoCreate : function(){
14493         var cfg = {
14494             tag: 'div',
14495             cls: 'progress'
14496         };
14497         
14498         
14499         if(this.striped){
14500             cfg.cls += ' progress-striped';
14501         }
14502       
14503         if(this.active){
14504             cfg.cls += ' active';
14505         }
14506         
14507         
14508         return cfg;
14509     }
14510    
14511 });
14512
14513  
14514
14515  /*
14516  * - LGPL
14517  *
14518  * ProgressBar
14519  * 
14520  */
14521
14522 /**
14523  * @class Roo.bootstrap.ProgressBar
14524  * @extends Roo.bootstrap.Component
14525  * Bootstrap ProgressBar class
14526  * @cfg {Number} aria_valuenow aria-value now
14527  * @cfg {Number} aria_valuemin aria-value min
14528  * @cfg {Number} aria_valuemax aria-value max
14529  * @cfg {String} label label for the progress bar
14530  * @cfg {String} panel (success | info | warning | danger )
14531  * @cfg {String} role role of the progress bar
14532  * @cfg {String} sr_only text
14533  * 
14534  * 
14535  * @constructor
14536  * Create a new ProgressBar
14537  * @param {Object} config The config object
14538  */
14539
14540 Roo.bootstrap.ProgressBar = function(config){
14541     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14542 };
14543
14544 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14545     
14546     aria_valuenow : 0,
14547     aria_valuemin : 0,
14548     aria_valuemax : 100,
14549     label : false,
14550     panel : false,
14551     role : false,
14552     sr_only: false,
14553     
14554     getAutoCreate : function()
14555     {
14556         
14557         var cfg = {
14558             tag: 'div',
14559             cls: 'progress-bar',
14560             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14561         };
14562         
14563         if(this.sr_only){
14564             cfg.cn = {
14565                 tag: 'span',
14566                 cls: 'sr-only',
14567                 html: this.sr_only
14568             }
14569         }
14570         
14571         if(this.role){
14572             cfg.role = this.role;
14573         }
14574         
14575         if(this.aria_valuenow){
14576             cfg['aria-valuenow'] = this.aria_valuenow;
14577         }
14578         
14579         if(this.aria_valuemin){
14580             cfg['aria-valuemin'] = this.aria_valuemin;
14581         }
14582         
14583         if(this.aria_valuemax){
14584             cfg['aria-valuemax'] = this.aria_valuemax;
14585         }
14586         
14587         if(this.label && !this.sr_only){
14588             cfg.html = this.label;
14589         }
14590         
14591         if(this.panel){
14592             cfg.cls += ' progress-bar-' + this.panel;
14593         }
14594         
14595         return cfg;
14596     },
14597     
14598     update : function(aria_valuenow)
14599     {
14600         this.aria_valuenow = aria_valuenow;
14601         
14602         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14603     }
14604    
14605 });
14606
14607  
14608
14609  /*
14610  * - LGPL
14611  *
14612  * column
14613  * 
14614  */
14615
14616 /**
14617  * @class Roo.bootstrap.TabGroup
14618  * @extends Roo.bootstrap.Column
14619  * Bootstrap Column class
14620  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14621  * @cfg {Boolean} carousel true to make the group behave like a carousel
14622  * @cfg {Number} bullets show the panel pointer.. default 0
14623  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
14624  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
14625  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14626  * 
14627  * @constructor
14628  * Create a new TabGroup
14629  * @param {Object} config The config object
14630  */
14631
14632 Roo.bootstrap.TabGroup = function(config){
14633     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14634     if (!this.navId) {
14635         this.navId = Roo.id();
14636     }
14637     this.tabs = [];
14638     Roo.bootstrap.TabGroup.register(this);
14639     
14640 };
14641
14642 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14643     
14644     carousel : false,
14645     transition : false,
14646     bullets : 0,
14647     timer : 0,
14648     autoslide : false,
14649     slideFn : false,
14650     slideOnTouch : false,
14651     
14652     getAutoCreate : function()
14653     {
14654         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14655         
14656         cfg.cls += ' tab-content';
14657         
14658         Roo.log('get auto create...............');
14659         
14660         if (this.carousel) {
14661             cfg.cls += ' carousel slide';
14662             
14663             cfg.cn = [{
14664                cls : 'carousel-inner'
14665             }];
14666         
14667             if(this.bullets > 0 && !Roo.isTouch){
14668                 
14669                 var bullets = {
14670                     cls : 'carousel-bullets',
14671                     cn : []
14672                 };
14673                 
14674                 if(this.bullets_cls){
14675                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14676                 }
14677                 
14678                 for (var i = 0; i < this.bullets; i++){
14679                     bullets.cn.push({
14680                         cls : 'bullet bullet-' + i
14681                     });
14682                 }
14683                 
14684                 bullets.cn.push({
14685                     cls : 'clear'
14686                 });
14687                 
14688                 cfg.cn[0].cn = bullets;
14689             }
14690         }
14691         
14692         return cfg;
14693     },
14694     
14695     initEvents:  function()
14696     {
14697         Roo.log('-------- init events on tab group ---------');
14698         
14699         if(this.bullets > 0 && !Roo.isTouch){
14700             this.initBullet();
14701         }
14702         
14703         Roo.log(this);
14704         
14705         if(Roo.isTouch && this.slideOnTouch){
14706             this.el.on("touchstart", this.onTouchStart, this);
14707         }
14708         
14709         if(this.autoslide){
14710             var _this = this;
14711             
14712             this.slideFn = window.setInterval(function() {
14713                 _this.showPanelNext();
14714             }, this.timer);
14715         }
14716         
14717     },
14718     
14719     onTouchStart : function(e, el, o)
14720     {
14721         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
14722             return;
14723         }
14724         
14725         this.showPanelNext();
14726     },
14727     
14728     getChildContainer : function()
14729     {
14730         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14731     },
14732     
14733     /**
14734     * register a Navigation item
14735     * @param {Roo.bootstrap.NavItem} the navitem to add
14736     */
14737     register : function(item)
14738     {
14739         this.tabs.push( item);
14740         item.navId = this.navId; // not really needed..
14741     
14742     },
14743     
14744     getActivePanel : function()
14745     {
14746         var r = false;
14747         Roo.each(this.tabs, function(t) {
14748             if (t.active) {
14749                 r = t;
14750                 return false;
14751             }
14752             return null;
14753         });
14754         return r;
14755         
14756     },
14757     getPanelByName : function(n)
14758     {
14759         var r = false;
14760         Roo.each(this.tabs, function(t) {
14761             if (t.tabId == n) {
14762                 r = t;
14763                 return false;
14764             }
14765             return null;
14766         });
14767         return r;
14768     },
14769     indexOfPanel : function(p)
14770     {
14771         var r = false;
14772         Roo.each(this.tabs, function(t,i) {
14773             if (t.tabId == p.tabId) {
14774                 r = i;
14775                 return false;
14776             }
14777             return null;
14778         });
14779         return r;
14780     },
14781     /**
14782      * show a specific panel
14783      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14784      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14785      */
14786     showPanel : function (pan)
14787     {
14788         if(this.transition){
14789             Roo.log("waiting for the transitionend");
14790             return;
14791         }
14792         
14793         if (typeof(pan) == 'number') {
14794             pan = this.tabs[pan];
14795         }
14796         if (typeof(pan) == 'string') {
14797             pan = this.getPanelByName(pan);
14798         }
14799         if (pan.tabId == this.getActivePanel().tabId) {
14800             return true;
14801         }
14802         var cur = this.getActivePanel();
14803         
14804         if (false === cur.fireEvent('beforedeactivate')) {
14805             return false;
14806         }
14807         
14808         if(this.bullets > 0 && !Roo.isTouch){
14809             this.setActiveBullet(this.indexOfPanel(pan));
14810         }
14811         
14812         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14813             
14814             this.transition = true;
14815             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14816             var lr = dir == 'next' ? 'left' : 'right';
14817             pan.el.addClass(dir); // or prev
14818             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14819             cur.el.addClass(lr); // or right
14820             pan.el.addClass(lr);
14821             
14822             var _this = this;
14823             cur.el.on('transitionend', function() {
14824                 Roo.log("trans end?");
14825                 
14826                 pan.el.removeClass([lr,dir]);
14827                 pan.setActive(true);
14828                 
14829                 cur.el.removeClass([lr]);
14830                 cur.setActive(false);
14831                 
14832                 _this.transition = false;
14833                 
14834             }, this, { single:  true } );
14835             
14836             return true;
14837         }
14838         
14839         cur.setActive(false);
14840         pan.setActive(true);
14841         
14842         return true;
14843         
14844     },
14845     showPanelNext : function()
14846     {
14847         var i = this.indexOfPanel(this.getActivePanel());
14848         
14849         if (i >= this.tabs.length - 1 && !this.autoslide) {
14850             return;
14851         }
14852         
14853         if (i >= this.tabs.length - 1 && this.autoslide) {
14854             i = -1;
14855         }
14856         
14857         this.showPanel(this.tabs[i+1]);
14858     },
14859     
14860     showPanelPrev : function()
14861     {
14862         var i = this.indexOfPanel(this.getActivePanel());
14863         
14864         if (i  < 1 && !this.autoslide) {
14865             return;
14866         }
14867         
14868         if (i < 1 && this.autoslide) {
14869             i = this.tabs.length;
14870         }
14871         
14872         this.showPanel(this.tabs[i-1]);
14873     },
14874     
14875     initBullet : function()
14876     {
14877         if(Roo.isTouch){
14878             return;
14879         }
14880         
14881         var _this = this;
14882         
14883         for (var i = 0; i < this.bullets; i++){
14884             var bullet = this.el.select('.bullet-' + i, true).first();
14885
14886             if(!bullet){
14887                 continue;
14888             }
14889
14890             bullet.on('click', (function(e, el, o, ii, t){
14891
14892                 e.preventDefault();
14893
14894                 _this.showPanel(ii);
14895
14896                 if(_this.autoslide && _this.slideFn){
14897                     clearInterval(_this.slideFn);
14898                     _this.slideFn = window.setInterval(function() {
14899                         _this.showPanelNext();
14900                     }, _this.timer);
14901                 }
14902
14903             }).createDelegate(this, [i, bullet], true));
14904         }
14905     },
14906     
14907     setActiveBullet : function(i)
14908     {
14909         if(Roo.isTouch){
14910             return;
14911         }
14912         
14913         Roo.each(this.el.select('.bullet', true).elements, function(el){
14914             el.removeClass('selected');
14915         });
14916
14917         var bullet = this.el.select('.bullet-' + i, true).first();
14918         
14919         if(!bullet){
14920             return;
14921         }
14922         
14923         bullet.addClass('selected');
14924     }
14925     
14926     
14927   
14928 });
14929
14930  
14931
14932  
14933  
14934 Roo.apply(Roo.bootstrap.TabGroup, {
14935     
14936     groups: {},
14937      /**
14938     * register a Navigation Group
14939     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14940     */
14941     register : function(navgrp)
14942     {
14943         this.groups[navgrp.navId] = navgrp;
14944         
14945     },
14946     /**
14947     * fetch a Navigation Group based on the navigation ID
14948     * if one does not exist , it will get created.
14949     * @param {string} the navgroup to add
14950     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14951     */
14952     get: function(navId) {
14953         if (typeof(this.groups[navId]) == 'undefined') {
14954             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14955         }
14956         return this.groups[navId] ;
14957     }
14958     
14959     
14960     
14961 });
14962
14963  /*
14964  * - LGPL
14965  *
14966  * TabPanel
14967  * 
14968  */
14969
14970 /**
14971  * @class Roo.bootstrap.TabPanel
14972  * @extends Roo.bootstrap.Component
14973  * Bootstrap TabPanel class
14974  * @cfg {Boolean} active panel active
14975  * @cfg {String} html panel content
14976  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14977  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14978  * 
14979  * 
14980  * @constructor
14981  * Create a new TabPanel
14982  * @param {Object} config The config object
14983  */
14984
14985 Roo.bootstrap.TabPanel = function(config){
14986     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14987     this.addEvents({
14988         /**
14989              * @event changed
14990              * Fires when the active status changes
14991              * @param {Roo.bootstrap.TabPanel} this
14992              * @param {Boolean} state the new state
14993             
14994          */
14995         'changed': true,
14996         /**
14997              * @event beforedeactivate
14998              * Fires before a tab is de-activated - can be used to do validation on a form.
14999              * @param {Roo.bootstrap.TabPanel} this
15000              * @return {Boolean} false if there is an error
15001             
15002          */
15003         'beforedeactivate': true
15004      });
15005     
15006     this.tabId = this.tabId || Roo.id();
15007   
15008 };
15009
15010 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15011     
15012     active: false,
15013     html: false,
15014     tabId: false,
15015     navId : false,
15016     
15017     getAutoCreate : function(){
15018         var cfg = {
15019             tag: 'div',
15020             // item is needed for carousel - not sure if it has any effect otherwise
15021             cls: 'tab-pane item',
15022             html: this.html || ''
15023         };
15024         
15025         if(this.active){
15026             cfg.cls += ' active';
15027         }
15028         
15029         if(this.tabId){
15030             cfg.tabId = this.tabId;
15031         }
15032         
15033         
15034         return cfg;
15035     },
15036     
15037     initEvents:  function()
15038     {
15039         Roo.log('-------- init events on tab panel ---------');
15040         
15041         var p = this.parent();
15042         this.navId = this.navId || p.navId;
15043         
15044         if (typeof(this.navId) != 'undefined') {
15045             // not really needed.. but just in case.. parent should be a NavGroup.
15046             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15047             Roo.log(['register', tg, this]);
15048             tg.register(this);
15049             
15050             var i = tg.tabs.length - 1;
15051             
15052             if(this.active && tg.bullets > 0 && i < tg.bullets){
15053                 tg.setActiveBullet(i);
15054             }
15055         }
15056         
15057     },
15058     
15059     
15060     onRender : function(ct, position)
15061     {
15062        // Roo.log("Call onRender: " + this.xtype);
15063         
15064         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15065         
15066         
15067         
15068         
15069         
15070     },
15071     
15072     setActive: function(state)
15073     {
15074         Roo.log("panel - set active " + this.tabId + "=" + state);
15075         
15076         this.active = state;
15077         if (!state) {
15078             this.el.removeClass('active');
15079             
15080         } else  if (!this.el.hasClass('active')) {
15081             this.el.addClass('active');
15082         }
15083         
15084         this.fireEvent('changed', this, state);
15085     }
15086     
15087     
15088 });
15089  
15090
15091  
15092
15093  /*
15094  * - LGPL
15095  *
15096  * DateField
15097  * 
15098  */
15099
15100 /**
15101  * @class Roo.bootstrap.DateField
15102  * @extends Roo.bootstrap.Input
15103  * Bootstrap DateField class
15104  * @cfg {Number} weekStart default 0
15105  * @cfg {String} viewMode default empty, (months|years)
15106  * @cfg {String} minViewMode default empty, (months|years)
15107  * @cfg {Number} startDate default -Infinity
15108  * @cfg {Number} endDate default Infinity
15109  * @cfg {Boolean} todayHighlight default false
15110  * @cfg {Boolean} todayBtn default false
15111  * @cfg {Boolean} calendarWeeks default false
15112  * @cfg {Object} daysOfWeekDisabled default empty
15113  * @cfg {Boolean} singleMode default false (true | false)
15114  * 
15115  * @cfg {Boolean} keyboardNavigation default true
15116  * @cfg {String} language default en
15117  * 
15118  * @constructor
15119  * Create a new DateField
15120  * @param {Object} config The config object
15121  */
15122
15123 Roo.bootstrap.DateField = function(config){
15124     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15125      this.addEvents({
15126             /**
15127              * @event show
15128              * Fires when this field show.
15129              * @param {Roo.bootstrap.DateField} this
15130              * @param {Mixed} date The date value
15131              */
15132             show : true,
15133             /**
15134              * @event show
15135              * Fires when this field hide.
15136              * @param {Roo.bootstrap.DateField} this
15137              * @param {Mixed} date The date value
15138              */
15139             hide : true,
15140             /**
15141              * @event select
15142              * Fires when select a date.
15143              * @param {Roo.bootstrap.DateField} this
15144              * @param {Mixed} date The date value
15145              */
15146             select : true
15147         });
15148 };
15149
15150 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15151     
15152     /**
15153      * @cfg {String} format
15154      * The default date format string which can be overriden for localization support.  The format must be
15155      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15156      */
15157     format : "m/d/y",
15158     /**
15159      * @cfg {String} altFormats
15160      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15161      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15162      */
15163     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15164     
15165     weekStart : 0,
15166     
15167     viewMode : '',
15168     
15169     minViewMode : '',
15170     
15171     todayHighlight : false,
15172     
15173     todayBtn: false,
15174     
15175     language: 'en',
15176     
15177     keyboardNavigation: true,
15178     
15179     calendarWeeks: false,
15180     
15181     startDate: -Infinity,
15182     
15183     endDate: Infinity,
15184     
15185     daysOfWeekDisabled: [],
15186     
15187     _events: [],
15188     
15189     singleMode : false,
15190     
15191     UTCDate: function()
15192     {
15193         return new Date(Date.UTC.apply(Date, arguments));
15194     },
15195     
15196     UTCToday: function()
15197     {
15198         var today = new Date();
15199         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15200     },
15201     
15202     getDate: function() {
15203             var d = this.getUTCDate();
15204             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15205     },
15206     
15207     getUTCDate: function() {
15208             return this.date;
15209     },
15210     
15211     setDate: function(d) {
15212             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15213     },
15214     
15215     setUTCDate: function(d) {
15216             this.date = d;
15217             this.setValue(this.formatDate(this.date));
15218     },
15219         
15220     onRender: function(ct, position)
15221     {
15222         
15223         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15224         
15225         this.language = this.language || 'en';
15226         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15227         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15228         
15229         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15230         this.format = this.format || 'm/d/y';
15231         this.isInline = false;
15232         this.isInput = true;
15233         this.component = this.el.select('.add-on', true).first() || false;
15234         this.component = (this.component && this.component.length === 0) ? false : this.component;
15235         this.hasInput = this.component && this.inputEL().length;
15236         
15237         if (typeof(this.minViewMode === 'string')) {
15238             switch (this.minViewMode) {
15239                 case 'months':
15240                     this.minViewMode = 1;
15241                     break;
15242                 case 'years':
15243                     this.minViewMode = 2;
15244                     break;
15245                 default:
15246                     this.minViewMode = 0;
15247                     break;
15248             }
15249         }
15250         
15251         if (typeof(this.viewMode === 'string')) {
15252             switch (this.viewMode) {
15253                 case 'months':
15254                     this.viewMode = 1;
15255                     break;
15256                 case 'years':
15257                     this.viewMode = 2;
15258                     break;
15259                 default:
15260                     this.viewMode = 0;
15261                     break;
15262             }
15263         }
15264                 
15265         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15266         
15267 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15268         
15269         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15270         
15271         this.picker().on('mousedown', this.onMousedown, this);
15272         this.picker().on('click', this.onClick, this);
15273         
15274         this.picker().addClass('datepicker-dropdown');
15275         
15276         this.startViewMode = this.viewMode;
15277         
15278         if(this.singleMode){
15279             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15280                 v.setVisibilityMode(Roo.Element.DISPLAY)
15281                 v.hide();
15282             });
15283             
15284             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15285                 v.setStyle('width', '189px');
15286             });
15287         }
15288         
15289         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15290             if(!this.calendarWeeks){
15291                 v.remove();
15292                 return;
15293             }
15294             
15295             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15296             v.attr('colspan', function(i, val){
15297                 return parseInt(val) + 1;
15298             });
15299         })
15300                         
15301         
15302         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15303         
15304         this.setStartDate(this.startDate);
15305         this.setEndDate(this.endDate);
15306         
15307         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15308         
15309         this.fillDow();
15310         this.fillMonths();
15311         this.update();
15312         this.showMode();
15313         
15314         if(this.isInline) {
15315             this.show();
15316         }
15317     },
15318     
15319     picker : function()
15320     {
15321         return this.pickerEl;
15322 //        return this.el.select('.datepicker', true).first();
15323     },
15324     
15325     fillDow: function()
15326     {
15327         var dowCnt = this.weekStart;
15328         
15329         var dow = {
15330             tag: 'tr',
15331             cn: [
15332                 
15333             ]
15334         };
15335         
15336         if(this.calendarWeeks){
15337             dow.cn.push({
15338                 tag: 'th',
15339                 cls: 'cw',
15340                 html: '&nbsp;'
15341             })
15342         }
15343         
15344         while (dowCnt < this.weekStart + 7) {
15345             dow.cn.push({
15346                 tag: 'th',
15347                 cls: 'dow',
15348                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15349             });
15350         }
15351         
15352         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15353     },
15354     
15355     fillMonths: function()
15356     {    
15357         var i = 0;
15358         var months = this.picker().select('>.datepicker-months td', true).first();
15359         
15360         months.dom.innerHTML = '';
15361         
15362         while (i < 12) {
15363             var month = {
15364                 tag: 'span',
15365                 cls: 'month',
15366                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15367             }
15368             
15369             months.createChild(month);
15370         }
15371         
15372     },
15373     
15374     update: function()
15375     {
15376         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;
15377         
15378         if (this.date < this.startDate) {
15379             this.viewDate = new Date(this.startDate);
15380         } else if (this.date > this.endDate) {
15381             this.viewDate = new Date(this.endDate);
15382         } else {
15383             this.viewDate = new Date(this.date);
15384         }
15385         
15386         this.fill();
15387     },
15388     
15389     fill: function() 
15390     {
15391         var d = new Date(this.viewDate),
15392                 year = d.getUTCFullYear(),
15393                 month = d.getUTCMonth(),
15394                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15395                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15396                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15397                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15398                 currentDate = this.date && this.date.valueOf(),
15399                 today = this.UTCToday();
15400         
15401         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15402         
15403 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15404         
15405 //        this.picker.select('>tfoot th.today').
15406 //                                              .text(dates[this.language].today)
15407 //                                              .toggle(this.todayBtn !== false);
15408     
15409         this.updateNavArrows();
15410         this.fillMonths();
15411                                                 
15412         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15413         
15414         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15415          
15416         prevMonth.setUTCDate(day);
15417         
15418         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15419         
15420         var nextMonth = new Date(prevMonth);
15421         
15422         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15423         
15424         nextMonth = nextMonth.valueOf();
15425         
15426         var fillMonths = false;
15427         
15428         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15429         
15430         while(prevMonth.valueOf() < nextMonth) {
15431             var clsName = '';
15432             
15433             if (prevMonth.getUTCDay() === this.weekStart) {
15434                 if(fillMonths){
15435                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15436                 }
15437                     
15438                 fillMonths = {
15439                     tag: 'tr',
15440                     cn: []
15441                 };
15442                 
15443                 if(this.calendarWeeks){
15444                     // ISO 8601: First week contains first thursday.
15445                     // ISO also states week starts on Monday, but we can be more abstract here.
15446                     var
15447                     // Start of current week: based on weekstart/current date
15448                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15449                     // Thursday of this week
15450                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15451                     // First Thursday of year, year from thursday
15452                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15453                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15454                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15455                     
15456                     fillMonths.cn.push({
15457                         tag: 'td',
15458                         cls: 'cw',
15459                         html: calWeek
15460                     });
15461                 }
15462             }
15463             
15464             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15465                 clsName += ' old';
15466             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15467                 clsName += ' new';
15468             }
15469             if (this.todayHighlight &&
15470                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15471                 prevMonth.getUTCMonth() == today.getMonth() &&
15472                 prevMonth.getUTCDate() == today.getDate()) {
15473                 clsName += ' today';
15474             }
15475             
15476             if (currentDate && prevMonth.valueOf() === currentDate) {
15477                 clsName += ' active';
15478             }
15479             
15480             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15481                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15482                     clsName += ' disabled';
15483             }
15484             
15485             fillMonths.cn.push({
15486                 tag: 'td',
15487                 cls: 'day ' + clsName,
15488                 html: prevMonth.getDate()
15489             })
15490             
15491             prevMonth.setDate(prevMonth.getDate()+1);
15492         }
15493           
15494         var currentYear = this.date && this.date.getUTCFullYear();
15495         var currentMonth = this.date && this.date.getUTCMonth();
15496         
15497         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15498         
15499         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15500             v.removeClass('active');
15501             
15502             if(currentYear === year && k === currentMonth){
15503                 v.addClass('active');
15504             }
15505             
15506             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15507                 v.addClass('disabled');
15508             }
15509             
15510         });
15511         
15512         
15513         year = parseInt(year/10, 10) * 10;
15514         
15515         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15516         
15517         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15518         
15519         year -= 1;
15520         for (var i = -1; i < 11; i++) {
15521             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15522                 tag: 'span',
15523                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15524                 html: year
15525             })
15526             
15527             year += 1;
15528         }
15529     },
15530     
15531     showMode: function(dir) 
15532     {
15533         if (dir) {
15534             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15535         }
15536         
15537         Roo.each(this.picker().select('>div',true).elements, function(v){
15538             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15539             v.hide();
15540         });
15541         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15542     },
15543     
15544     place: function()
15545     {
15546         if(this.isInline) return;
15547         
15548         this.picker().removeClass(['bottom', 'top']);
15549         
15550         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15551             /*
15552              * place to the top of element!
15553              *
15554              */
15555             
15556             this.picker().addClass('top');
15557             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15558             
15559             return;
15560         }
15561         
15562         this.picker().addClass('bottom');
15563         
15564         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15565     },
15566     
15567     parseDate : function(value)
15568     {
15569         if(!value || value instanceof Date){
15570             return value;
15571         }
15572         var v = Date.parseDate(value, this.format);
15573         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15574             v = Date.parseDate(value, 'Y-m-d');
15575         }
15576         if(!v && this.altFormats){
15577             if(!this.altFormatsArray){
15578                 this.altFormatsArray = this.altFormats.split("|");
15579             }
15580             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15581                 v = Date.parseDate(value, this.altFormatsArray[i]);
15582             }
15583         }
15584         return v;
15585     },
15586     
15587     formatDate : function(date, fmt)
15588     {   
15589         return (!date || !(date instanceof Date)) ?
15590         date : date.dateFormat(fmt || this.format);
15591     },
15592     
15593     onFocus : function()
15594     {
15595         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15596         this.show();
15597     },
15598     
15599     onBlur : function()
15600     {
15601         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15602         
15603         var d = this.inputEl().getValue();
15604         
15605         this.setValue(d);
15606                 
15607         this.hide();
15608     },
15609     
15610     show : function()
15611     {
15612         this.picker().show();
15613         this.update();
15614         this.place();
15615         
15616         this.fireEvent('show', this, this.date);
15617     },
15618     
15619     hide : function()
15620     {
15621         if(this.isInline) return;
15622         this.picker().hide();
15623         this.viewMode = this.startViewMode;
15624         this.showMode();
15625         
15626         this.fireEvent('hide', this, this.date);
15627         
15628     },
15629     
15630     onMousedown: function(e)
15631     {
15632         e.stopPropagation();
15633         e.preventDefault();
15634     },
15635     
15636     keyup: function(e)
15637     {
15638         Roo.bootstrap.DateField.superclass.keyup.call(this);
15639         this.update();
15640     },
15641
15642     setValue: function(v)
15643     {
15644         
15645         // v can be a string or a date..
15646         
15647         
15648         var d = new Date(this.parseDate(v) ).clearTime();
15649         
15650         if(isNaN(d.getTime())){
15651             this.date = this.viewDate = '';
15652             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15653             return;
15654         }
15655         
15656         v = this.formatDate(d);
15657         
15658         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15659         
15660         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15661      
15662         this.update();
15663
15664         this.fireEvent('select', this, this.date);
15665         
15666     },
15667     
15668     getValue: function()
15669     {
15670         return this.formatDate(this.date);
15671     },
15672     
15673     fireKey: function(e)
15674     {
15675         if (!this.picker().isVisible()){
15676             if (e.keyCode == 27) // allow escape to hide and re-show picker
15677                 this.show();
15678             return;
15679         }
15680         
15681         var dateChanged = false,
15682         dir, day, month,
15683         newDate, newViewDate;
15684         
15685         switch(e.keyCode){
15686             case 27: // escape
15687                 this.hide();
15688                 e.preventDefault();
15689                 break;
15690             case 37: // left
15691             case 39: // right
15692                 if (!this.keyboardNavigation) break;
15693                 dir = e.keyCode == 37 ? -1 : 1;
15694                 
15695                 if (e.ctrlKey){
15696                     newDate = this.moveYear(this.date, dir);
15697                     newViewDate = this.moveYear(this.viewDate, dir);
15698                 } else if (e.shiftKey){
15699                     newDate = this.moveMonth(this.date, dir);
15700                     newViewDate = this.moveMonth(this.viewDate, dir);
15701                 } else {
15702                     newDate = new Date(this.date);
15703                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15704                     newViewDate = new Date(this.viewDate);
15705                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15706                 }
15707                 if (this.dateWithinRange(newDate)){
15708                     this.date = newDate;
15709                     this.viewDate = newViewDate;
15710                     this.setValue(this.formatDate(this.date));
15711 //                    this.update();
15712                     e.preventDefault();
15713                     dateChanged = true;
15714                 }
15715                 break;
15716             case 38: // up
15717             case 40: // down
15718                 if (!this.keyboardNavigation) break;
15719                 dir = e.keyCode == 38 ? -1 : 1;
15720                 if (e.ctrlKey){
15721                     newDate = this.moveYear(this.date, dir);
15722                     newViewDate = this.moveYear(this.viewDate, dir);
15723                 } else if (e.shiftKey){
15724                     newDate = this.moveMonth(this.date, dir);
15725                     newViewDate = this.moveMonth(this.viewDate, dir);
15726                 } else {
15727                     newDate = new Date(this.date);
15728                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15729                     newViewDate = new Date(this.viewDate);
15730                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15731                 }
15732                 if (this.dateWithinRange(newDate)){
15733                     this.date = newDate;
15734                     this.viewDate = newViewDate;
15735                     this.setValue(this.formatDate(this.date));
15736 //                    this.update();
15737                     e.preventDefault();
15738                     dateChanged = true;
15739                 }
15740                 break;
15741             case 13: // enter
15742                 this.setValue(this.formatDate(this.date));
15743                 this.hide();
15744                 e.preventDefault();
15745                 break;
15746             case 9: // tab
15747                 this.setValue(this.formatDate(this.date));
15748                 this.hide();
15749                 break;
15750             case 16: // shift
15751             case 17: // ctrl
15752             case 18: // alt
15753                 break;
15754             default :
15755                 this.hide();
15756                 
15757         }
15758     },
15759     
15760     
15761     onClick: function(e) 
15762     {
15763         e.stopPropagation();
15764         e.preventDefault();
15765         
15766         var target = e.getTarget();
15767         
15768         if(target.nodeName.toLowerCase() === 'i'){
15769             target = Roo.get(target).dom.parentNode;
15770         }
15771         
15772         var nodeName = target.nodeName;
15773         var className = target.className;
15774         var html = target.innerHTML;
15775         //Roo.log(nodeName);
15776         
15777         switch(nodeName.toLowerCase()) {
15778             case 'th':
15779                 switch(className) {
15780                     case 'switch':
15781                         this.showMode(1);
15782                         break;
15783                     case 'prev':
15784                     case 'next':
15785                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15786                         switch(this.viewMode){
15787                                 case 0:
15788                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15789                                         break;
15790                                 case 1:
15791                                 case 2:
15792                                         this.viewDate = this.moveYear(this.viewDate, dir);
15793                                         break;
15794                         }
15795                         this.fill();
15796                         break;
15797                     case 'today':
15798                         var date = new Date();
15799                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15800 //                        this.fill()
15801                         this.setValue(this.formatDate(this.date));
15802                         
15803                         this.hide();
15804                         break;
15805                 }
15806                 break;
15807             case 'span':
15808                 if (className.indexOf('disabled') < 0) {
15809                     this.viewDate.setUTCDate(1);
15810                     if (className.indexOf('month') > -1) {
15811                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15812                     } else {
15813                         var year = parseInt(html, 10) || 0;
15814                         this.viewDate.setUTCFullYear(year);
15815                         
15816                     }
15817                     
15818                     if(this.singleMode){
15819                         this.setValue(this.formatDate(this.viewDate));
15820                         this.hide();
15821                         return;
15822                     }
15823                     
15824                     this.showMode(-1);
15825                     this.fill();
15826                 }
15827                 break;
15828                 
15829             case 'td':
15830                 //Roo.log(className);
15831                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15832                     var day = parseInt(html, 10) || 1;
15833                     var year = this.viewDate.getUTCFullYear(),
15834                         month = this.viewDate.getUTCMonth();
15835
15836                     if (className.indexOf('old') > -1) {
15837                         if(month === 0 ){
15838                             month = 11;
15839                             year -= 1;
15840                         }else{
15841                             month -= 1;
15842                         }
15843                     } else if (className.indexOf('new') > -1) {
15844                         if (month == 11) {
15845                             month = 0;
15846                             year += 1;
15847                         } else {
15848                             month += 1;
15849                         }
15850                     }
15851                     //Roo.log([year,month,day]);
15852                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15853                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15854 //                    this.fill();
15855                     //Roo.log(this.formatDate(this.date));
15856                     this.setValue(this.formatDate(this.date));
15857                     this.hide();
15858                 }
15859                 break;
15860         }
15861     },
15862     
15863     setStartDate: function(startDate)
15864     {
15865         this.startDate = startDate || -Infinity;
15866         if (this.startDate !== -Infinity) {
15867             this.startDate = this.parseDate(this.startDate);
15868         }
15869         this.update();
15870         this.updateNavArrows();
15871     },
15872
15873     setEndDate: function(endDate)
15874     {
15875         this.endDate = endDate || Infinity;
15876         if (this.endDate !== Infinity) {
15877             this.endDate = this.parseDate(this.endDate);
15878         }
15879         this.update();
15880         this.updateNavArrows();
15881     },
15882     
15883     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15884     {
15885         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15886         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15887             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15888         }
15889         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15890             return parseInt(d, 10);
15891         });
15892         this.update();
15893         this.updateNavArrows();
15894     },
15895     
15896     updateNavArrows: function() 
15897     {
15898         if(this.singleMode){
15899             return;
15900         }
15901         
15902         var d = new Date(this.viewDate),
15903         year = d.getUTCFullYear(),
15904         month = d.getUTCMonth();
15905         
15906         Roo.each(this.picker().select('.prev', true).elements, function(v){
15907             v.show();
15908             switch (this.viewMode) {
15909                 case 0:
15910
15911                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15912                         v.hide();
15913                     }
15914                     break;
15915                 case 1:
15916                 case 2:
15917                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15918                         v.hide();
15919                     }
15920                     break;
15921             }
15922         });
15923         
15924         Roo.each(this.picker().select('.next', true).elements, function(v){
15925             v.show();
15926             switch (this.viewMode) {
15927                 case 0:
15928
15929                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15930                         v.hide();
15931                     }
15932                     break;
15933                 case 1:
15934                 case 2:
15935                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15936                         v.hide();
15937                     }
15938                     break;
15939             }
15940         })
15941     },
15942     
15943     moveMonth: function(date, dir)
15944     {
15945         if (!dir) return date;
15946         var new_date = new Date(date.valueOf()),
15947         day = new_date.getUTCDate(),
15948         month = new_date.getUTCMonth(),
15949         mag = Math.abs(dir),
15950         new_month, test;
15951         dir = dir > 0 ? 1 : -1;
15952         if (mag == 1){
15953             test = dir == -1
15954             // If going back one month, make sure month is not current month
15955             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15956             ? function(){
15957                 return new_date.getUTCMonth() == month;
15958             }
15959             // If going forward one month, make sure month is as expected
15960             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15961             : function(){
15962                 return new_date.getUTCMonth() != new_month;
15963             };
15964             new_month = month + dir;
15965             new_date.setUTCMonth(new_month);
15966             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15967             if (new_month < 0 || new_month > 11)
15968                 new_month = (new_month + 12) % 12;
15969         } else {
15970             // For magnitudes >1, move one month at a time...
15971             for (var i=0; i<mag; i++)
15972                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15973                 new_date = this.moveMonth(new_date, dir);
15974             // ...then reset the day, keeping it in the new month
15975             new_month = new_date.getUTCMonth();
15976             new_date.setUTCDate(day);
15977             test = function(){
15978                 return new_month != new_date.getUTCMonth();
15979             };
15980         }
15981         // Common date-resetting loop -- if date is beyond end of month, make it
15982         // end of month
15983         while (test()){
15984             new_date.setUTCDate(--day);
15985             new_date.setUTCMonth(new_month);
15986         }
15987         return new_date;
15988     },
15989
15990     moveYear: function(date, dir)
15991     {
15992         return this.moveMonth(date, dir*12);
15993     },
15994
15995     dateWithinRange: function(date)
15996     {
15997         return date >= this.startDate && date <= this.endDate;
15998     },
15999
16000     
16001     remove: function() 
16002     {
16003         this.picker().remove();
16004     }
16005    
16006 });
16007
16008 Roo.apply(Roo.bootstrap.DateField,  {
16009     
16010     head : {
16011         tag: 'thead',
16012         cn: [
16013         {
16014             tag: 'tr',
16015             cn: [
16016             {
16017                 tag: 'th',
16018                 cls: 'prev',
16019                 html: '<i class="fa fa-arrow-left"/>'
16020             },
16021             {
16022                 tag: 'th',
16023                 cls: 'switch',
16024                 colspan: '5'
16025             },
16026             {
16027                 tag: 'th',
16028                 cls: 'next',
16029                 html: '<i class="fa fa-arrow-right"/>'
16030             }
16031
16032             ]
16033         }
16034         ]
16035     },
16036     
16037     content : {
16038         tag: 'tbody',
16039         cn: [
16040         {
16041             tag: 'tr',
16042             cn: [
16043             {
16044                 tag: 'td',
16045                 colspan: '7'
16046             }
16047             ]
16048         }
16049         ]
16050     },
16051     
16052     footer : {
16053         tag: 'tfoot',
16054         cn: [
16055         {
16056             tag: 'tr',
16057             cn: [
16058             {
16059                 tag: 'th',
16060                 colspan: '7',
16061                 cls: 'today'
16062             }
16063                     
16064             ]
16065         }
16066         ]
16067     },
16068     
16069     dates:{
16070         en: {
16071             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16072             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16073             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16074             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16075             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16076             today: "Today"
16077         }
16078     },
16079     
16080     modes: [
16081     {
16082         clsName: 'days',
16083         navFnc: 'Month',
16084         navStep: 1
16085     },
16086     {
16087         clsName: 'months',
16088         navFnc: 'FullYear',
16089         navStep: 1
16090     },
16091     {
16092         clsName: 'years',
16093         navFnc: 'FullYear',
16094         navStep: 10
16095     }]
16096 });
16097
16098 Roo.apply(Roo.bootstrap.DateField,  {
16099   
16100     template : {
16101         tag: 'div',
16102         cls: 'datepicker dropdown-menu roo-dynamic',
16103         cn: [
16104         {
16105             tag: 'div',
16106             cls: 'datepicker-days',
16107             cn: [
16108             {
16109                 tag: 'table',
16110                 cls: 'table-condensed',
16111                 cn:[
16112                 Roo.bootstrap.DateField.head,
16113                 {
16114                     tag: 'tbody'
16115                 },
16116                 Roo.bootstrap.DateField.footer
16117                 ]
16118             }
16119             ]
16120         },
16121         {
16122             tag: 'div',
16123             cls: 'datepicker-months',
16124             cn: [
16125             {
16126                 tag: 'table',
16127                 cls: 'table-condensed',
16128                 cn:[
16129                 Roo.bootstrap.DateField.head,
16130                 Roo.bootstrap.DateField.content,
16131                 Roo.bootstrap.DateField.footer
16132                 ]
16133             }
16134             ]
16135         },
16136         {
16137             tag: 'div',
16138             cls: 'datepicker-years',
16139             cn: [
16140             {
16141                 tag: 'table',
16142                 cls: 'table-condensed',
16143                 cn:[
16144                 Roo.bootstrap.DateField.head,
16145                 Roo.bootstrap.DateField.content,
16146                 Roo.bootstrap.DateField.footer
16147                 ]
16148             }
16149             ]
16150         }
16151         ]
16152     }
16153 });
16154
16155  
16156
16157  /*
16158  * - LGPL
16159  *
16160  * TimeField
16161  * 
16162  */
16163
16164 /**
16165  * @class Roo.bootstrap.TimeField
16166  * @extends Roo.bootstrap.Input
16167  * Bootstrap DateField class
16168  * 
16169  * 
16170  * @constructor
16171  * Create a new TimeField
16172  * @param {Object} config The config object
16173  */
16174
16175 Roo.bootstrap.TimeField = function(config){
16176     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16177     this.addEvents({
16178             /**
16179              * @event show
16180              * Fires when this field show.
16181              * @param {Roo.bootstrap.DateField} thisthis
16182              * @param {Mixed} date The date value
16183              */
16184             show : true,
16185             /**
16186              * @event show
16187              * Fires when this field hide.
16188              * @param {Roo.bootstrap.DateField} this
16189              * @param {Mixed} date The date value
16190              */
16191             hide : true,
16192             /**
16193              * @event select
16194              * Fires when select a date.
16195              * @param {Roo.bootstrap.DateField} this
16196              * @param {Mixed} date The date value
16197              */
16198             select : true
16199         });
16200 };
16201
16202 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16203     
16204     /**
16205      * @cfg {String} format
16206      * The default time format string which can be overriden for localization support.  The format must be
16207      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16208      */
16209     format : "H:i",
16210        
16211     onRender: function(ct, position)
16212     {
16213         
16214         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16215                 
16216         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16217         
16218         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16219         
16220         this.pop = this.picker().select('>.datepicker-time',true).first();
16221         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16222         
16223         this.picker().on('mousedown', this.onMousedown, this);
16224         this.picker().on('click', this.onClick, this);
16225         
16226         this.picker().addClass('datepicker-dropdown');
16227     
16228         this.fillTime();
16229         this.update();
16230             
16231         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16232         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16233         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16234         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16235         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16236         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16237
16238     },
16239     
16240     fireKey: function(e){
16241         if (!this.picker().isVisible()){
16242             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16243                 this.show();
16244             }
16245             return;
16246         }
16247
16248         e.preventDefault();
16249         
16250         switch(e.keyCode){
16251             case 27: // escape
16252                 this.hide();
16253                 break;
16254             case 37: // left
16255             case 39: // right
16256                 this.onTogglePeriod();
16257                 break;
16258             case 38: // up
16259                 this.onIncrementMinutes();
16260                 break;
16261             case 40: // down
16262                 this.onDecrementMinutes();
16263                 break;
16264             case 13: // enter
16265             case 9: // tab
16266                 this.setTime();
16267                 break;
16268         }
16269     },
16270     
16271     onClick: function(e) {
16272         e.stopPropagation();
16273         e.preventDefault();
16274     },
16275     
16276     picker : function()
16277     {
16278         return this.el.select('.datepicker', true).first();
16279     },
16280     
16281     fillTime: function()
16282     {    
16283         var time = this.pop.select('tbody', true).first();
16284         
16285         time.dom.innerHTML = '';
16286         
16287         time.createChild({
16288             tag: 'tr',
16289             cn: [
16290                 {
16291                     tag: 'td',
16292                     cn: [
16293                         {
16294                             tag: 'a',
16295                             href: '#',
16296                             cls: 'btn',
16297                             cn: [
16298                                 {
16299                                     tag: 'span',
16300                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16301                                 }
16302                             ]
16303                         } 
16304                     ]
16305                 },
16306                 {
16307                     tag: 'td',
16308                     cls: 'separator'
16309                 },
16310                 {
16311                     tag: 'td',
16312                     cn: [
16313                         {
16314                             tag: 'a',
16315                             href: '#',
16316                             cls: 'btn',
16317                             cn: [
16318                                 {
16319                                     tag: 'span',
16320                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16321                                 }
16322                             ]
16323                         }
16324                     ]
16325                 },
16326                 {
16327                     tag: 'td',
16328                     cls: 'separator'
16329                 }
16330             ]
16331         });
16332         
16333         time.createChild({
16334             tag: 'tr',
16335             cn: [
16336                 {
16337                     tag: 'td',
16338                     cn: [
16339                         {
16340                             tag: 'span',
16341                             cls: 'timepicker-hour',
16342                             html: '00'
16343                         }  
16344                     ]
16345                 },
16346                 {
16347                     tag: 'td',
16348                     cls: 'separator',
16349                     html: ':'
16350                 },
16351                 {
16352                     tag: 'td',
16353                     cn: [
16354                         {
16355                             tag: 'span',
16356                             cls: 'timepicker-minute',
16357                             html: '00'
16358                         }  
16359                     ]
16360                 },
16361                 {
16362                     tag: 'td',
16363                     cls: 'separator'
16364                 },
16365                 {
16366                     tag: 'td',
16367                     cn: [
16368                         {
16369                             tag: 'button',
16370                             type: 'button',
16371                             cls: 'btn btn-primary period',
16372                             html: 'AM'
16373                             
16374                         }
16375                     ]
16376                 }
16377             ]
16378         });
16379         
16380         time.createChild({
16381             tag: 'tr',
16382             cn: [
16383                 {
16384                     tag: 'td',
16385                     cn: [
16386                         {
16387                             tag: 'a',
16388                             href: '#',
16389                             cls: 'btn',
16390                             cn: [
16391                                 {
16392                                     tag: 'span',
16393                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16394                                 }
16395                             ]
16396                         }
16397                     ]
16398                 },
16399                 {
16400                     tag: 'td',
16401                     cls: 'separator'
16402                 },
16403                 {
16404                     tag: 'td',
16405                     cn: [
16406                         {
16407                             tag: 'a',
16408                             href: '#',
16409                             cls: 'btn',
16410                             cn: [
16411                                 {
16412                                     tag: 'span',
16413                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16414                                 }
16415                             ]
16416                         }
16417                     ]
16418                 },
16419                 {
16420                     tag: 'td',
16421                     cls: 'separator'
16422                 }
16423             ]
16424         });
16425         
16426     },
16427     
16428     update: function()
16429     {
16430         
16431         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16432         
16433         this.fill();
16434     },
16435     
16436     fill: function() 
16437     {
16438         var hours = this.time.getHours();
16439         var minutes = this.time.getMinutes();
16440         var period = 'AM';
16441         
16442         if(hours > 11){
16443             period = 'PM';
16444         }
16445         
16446         if(hours == 0){
16447             hours = 12;
16448         }
16449         
16450         
16451         if(hours > 12){
16452             hours = hours - 12;
16453         }
16454         
16455         if(hours < 10){
16456             hours = '0' + hours;
16457         }
16458         
16459         if(minutes < 10){
16460             minutes = '0' + minutes;
16461         }
16462         
16463         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16464         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16465         this.pop.select('button', true).first().dom.innerHTML = period;
16466         
16467     },
16468     
16469     place: function()
16470     {   
16471         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16472         
16473         var cls = ['bottom'];
16474         
16475         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16476             cls.pop();
16477             cls.push('top');
16478         }
16479         
16480         cls.push('right');
16481         
16482         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16483             cls.pop();
16484             cls.push('left');
16485         }
16486         
16487         this.picker().addClass(cls.join('-'));
16488         
16489         var _this = this;
16490         
16491         Roo.each(cls, function(c){
16492             if(c == 'bottom'){
16493                 _this.picker().setTop(_this.inputEl().getHeight());
16494                 return;
16495             }
16496             if(c == 'top'){
16497                 _this.picker().setTop(0 - _this.picker().getHeight());
16498                 return;
16499             }
16500             
16501             if(c == 'left'){
16502                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16503                 return;
16504             }
16505             if(c == 'right'){
16506                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16507                 return;
16508             }
16509         });
16510         
16511     },
16512   
16513     onFocus : function()
16514     {
16515         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16516         this.show();
16517     },
16518     
16519     onBlur : function()
16520     {
16521         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16522         this.hide();
16523     },
16524     
16525     show : function()
16526     {
16527         this.picker().show();
16528         this.pop.show();
16529         this.update();
16530         this.place();
16531         
16532         this.fireEvent('show', this, this.date);
16533     },
16534     
16535     hide : function()
16536     {
16537         this.picker().hide();
16538         this.pop.hide();
16539         
16540         this.fireEvent('hide', this, this.date);
16541     },
16542     
16543     setTime : function()
16544     {
16545         this.hide();
16546         this.setValue(this.time.format(this.format));
16547         
16548         this.fireEvent('select', this, this.date);
16549         
16550         
16551     },
16552     
16553     onMousedown: function(e){
16554         e.stopPropagation();
16555         e.preventDefault();
16556     },
16557     
16558     onIncrementHours: function()
16559     {
16560         Roo.log('onIncrementHours');
16561         this.time = this.time.add(Date.HOUR, 1);
16562         this.update();
16563         
16564     },
16565     
16566     onDecrementHours: function()
16567     {
16568         Roo.log('onDecrementHours');
16569         this.time = this.time.add(Date.HOUR, -1);
16570         this.update();
16571     },
16572     
16573     onIncrementMinutes: function()
16574     {
16575         Roo.log('onIncrementMinutes');
16576         this.time = this.time.add(Date.MINUTE, 1);
16577         this.update();
16578     },
16579     
16580     onDecrementMinutes: function()
16581     {
16582         Roo.log('onDecrementMinutes');
16583         this.time = this.time.add(Date.MINUTE, -1);
16584         this.update();
16585     },
16586     
16587     onTogglePeriod: function()
16588     {
16589         Roo.log('onTogglePeriod');
16590         this.time = this.time.add(Date.HOUR, 12);
16591         this.update();
16592     }
16593     
16594    
16595 });
16596
16597 Roo.apply(Roo.bootstrap.TimeField,  {
16598     
16599     content : {
16600         tag: 'tbody',
16601         cn: [
16602             {
16603                 tag: 'tr',
16604                 cn: [
16605                 {
16606                     tag: 'td',
16607                     colspan: '7'
16608                 }
16609                 ]
16610             }
16611         ]
16612     },
16613     
16614     footer : {
16615         tag: 'tfoot',
16616         cn: [
16617             {
16618                 tag: 'tr',
16619                 cn: [
16620                 {
16621                     tag: 'th',
16622                     colspan: '7',
16623                     cls: '',
16624                     cn: [
16625                         {
16626                             tag: 'button',
16627                             cls: 'btn btn-info ok',
16628                             html: 'OK'
16629                         }
16630                     ]
16631                 }
16632
16633                 ]
16634             }
16635         ]
16636     }
16637 });
16638
16639 Roo.apply(Roo.bootstrap.TimeField,  {
16640   
16641     template : {
16642         tag: 'div',
16643         cls: 'datepicker dropdown-menu',
16644         cn: [
16645             {
16646                 tag: 'div',
16647                 cls: 'datepicker-time',
16648                 cn: [
16649                 {
16650                     tag: 'table',
16651                     cls: 'table-condensed',
16652                     cn:[
16653                     Roo.bootstrap.TimeField.content,
16654                     Roo.bootstrap.TimeField.footer
16655                     ]
16656                 }
16657                 ]
16658             }
16659         ]
16660     }
16661 });
16662
16663  
16664
16665  /*
16666  * - LGPL
16667  *
16668  * MonthField
16669  * 
16670  */
16671
16672 /**
16673  * @class Roo.bootstrap.MonthField
16674  * @extends Roo.bootstrap.Input
16675  * Bootstrap MonthField class
16676  * 
16677  * @cfg {String} language default en
16678  * 
16679  * @constructor
16680  * Create a new MonthField
16681  * @param {Object} config The config object
16682  */
16683
16684 Roo.bootstrap.MonthField = function(config){
16685     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16686     
16687     this.addEvents({
16688         /**
16689          * @event show
16690          * Fires when this field show.
16691          * @param {Roo.bootstrap.MonthField} this
16692          * @param {Mixed} date The date value
16693          */
16694         show : true,
16695         /**
16696          * @event show
16697          * Fires when this field hide.
16698          * @param {Roo.bootstrap.MonthField} this
16699          * @param {Mixed} date The date value
16700          */
16701         hide : true,
16702         /**
16703          * @event select
16704          * Fires when select a date.
16705          * @param {Roo.bootstrap.MonthField} this
16706          * @param {String} oldvalue The old value
16707          * @param {String} newvalue The new value
16708          */
16709         select : true
16710     });
16711 };
16712
16713 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16714     
16715     onRender: function(ct, position)
16716     {
16717         
16718         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16719         
16720         this.language = this.language || 'en';
16721         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16722         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16723         
16724         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16725         this.isInline = false;
16726         this.isInput = true;
16727         this.component = this.el.select('.add-on', true).first() || false;
16728         this.component = (this.component && this.component.length === 0) ? false : this.component;
16729         this.hasInput = this.component && this.inputEL().length;
16730         
16731         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16732         
16733         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16734         
16735         this.picker().on('mousedown', this.onMousedown, this);
16736         this.picker().on('click', this.onClick, this);
16737         
16738         this.picker().addClass('datepicker-dropdown');
16739         
16740         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16741             v.setStyle('width', '189px');
16742         });
16743         
16744         this.fillMonths();
16745         
16746         this.update();
16747         
16748         if(this.isInline) {
16749             this.show();
16750         }
16751         
16752     },
16753     
16754     setValue: function(v, suppressEvent)
16755     {   
16756         var o = this.getValue();
16757         
16758         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16759         
16760         this.update();
16761
16762         if(suppressEvent !== true){
16763             this.fireEvent('select', this, o, v);
16764         }
16765         
16766     },
16767     
16768     getValue: function()
16769     {
16770         return this.value;
16771     },
16772     
16773     onClick: function(e) 
16774     {
16775         e.stopPropagation();
16776         e.preventDefault();
16777         
16778         var target = e.getTarget();
16779         
16780         if(target.nodeName.toLowerCase() === 'i'){
16781             target = Roo.get(target).dom.parentNode;
16782         }
16783         
16784         var nodeName = target.nodeName;
16785         var className = target.className;
16786         var html = target.innerHTML;
16787         
16788         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16789             return;
16790         }
16791         
16792         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16793         
16794         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16795         
16796         this.hide();
16797                         
16798     },
16799     
16800     picker : function()
16801     {
16802         return this.pickerEl;
16803     },
16804     
16805     fillMonths: function()
16806     {    
16807         var i = 0;
16808         var months = this.picker().select('>.datepicker-months td', true).first();
16809         
16810         months.dom.innerHTML = '';
16811         
16812         while (i < 12) {
16813             var month = {
16814                 tag: 'span',
16815                 cls: 'month',
16816                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16817             }
16818             
16819             months.createChild(month);
16820         }
16821         
16822     },
16823     
16824     update: function()
16825     {
16826         var _this = this;
16827         
16828         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16829             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16830         }
16831         
16832         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16833             e.removeClass('active');
16834             
16835             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16836                 e.addClass('active');
16837             }
16838         })
16839     },
16840     
16841     place: function()
16842     {
16843         if(this.isInline) return;
16844         
16845         this.picker().removeClass(['bottom', 'top']);
16846         
16847         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16848             /*
16849              * place to the top of element!
16850              *
16851              */
16852             
16853             this.picker().addClass('top');
16854             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16855             
16856             return;
16857         }
16858         
16859         this.picker().addClass('bottom');
16860         
16861         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16862     },
16863     
16864     onFocus : function()
16865     {
16866         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16867         this.show();
16868     },
16869     
16870     onBlur : function()
16871     {
16872         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16873         
16874         var d = this.inputEl().getValue();
16875         
16876         this.setValue(d);
16877                 
16878         this.hide();
16879     },
16880     
16881     show : function()
16882     {
16883         this.picker().show();
16884         this.picker().select('>.datepicker-months', true).first().show();
16885         this.update();
16886         this.place();
16887         
16888         this.fireEvent('show', this, this.date);
16889     },
16890     
16891     hide : function()
16892     {
16893         if(this.isInline) return;
16894         this.picker().hide();
16895         this.fireEvent('hide', this, this.date);
16896         
16897     },
16898     
16899     onMousedown: function(e)
16900     {
16901         e.stopPropagation();
16902         e.preventDefault();
16903     },
16904     
16905     keyup: function(e)
16906     {
16907         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16908         this.update();
16909     },
16910
16911     fireKey: function(e)
16912     {
16913         if (!this.picker().isVisible()){
16914             if (e.keyCode == 27) // allow escape to hide and re-show picker
16915                 this.show();
16916             return;
16917         }
16918         
16919         var dir;
16920         
16921         switch(e.keyCode){
16922             case 27: // escape
16923                 this.hide();
16924                 e.preventDefault();
16925                 break;
16926             case 37: // left
16927             case 39: // right
16928                 dir = e.keyCode == 37 ? -1 : 1;
16929                 
16930                 this.vIndex = this.vIndex + dir;
16931                 
16932                 if(this.vIndex < 0){
16933                     this.vIndex = 0;
16934                 }
16935                 
16936                 if(this.vIndex > 11){
16937                     this.vIndex = 11;
16938                 }
16939                 
16940                 if(isNaN(this.vIndex)){
16941                     this.vIndex = 0;
16942                 }
16943                 
16944                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16945                 
16946                 break;
16947             case 38: // up
16948             case 40: // down
16949                 
16950                 dir = e.keyCode == 38 ? -1 : 1;
16951                 
16952                 this.vIndex = this.vIndex + dir * 4;
16953                 
16954                 if(this.vIndex < 0){
16955                     this.vIndex = 0;
16956                 }
16957                 
16958                 if(this.vIndex > 11){
16959                     this.vIndex = 11;
16960                 }
16961                 
16962                 if(isNaN(this.vIndex)){
16963                     this.vIndex = 0;
16964                 }
16965                 
16966                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16967                 break;
16968                 
16969             case 13: // enter
16970                 
16971                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16972                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16973                 }
16974                 
16975                 this.hide();
16976                 e.preventDefault();
16977                 break;
16978             case 9: // tab
16979                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16980                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16981                 }
16982                 this.hide();
16983                 break;
16984             case 16: // shift
16985             case 17: // ctrl
16986             case 18: // alt
16987                 break;
16988             default :
16989                 this.hide();
16990                 
16991         }
16992     },
16993     
16994     remove: function() 
16995     {
16996         this.picker().remove();
16997     }
16998    
16999 });
17000
17001 Roo.apply(Roo.bootstrap.MonthField,  {
17002     
17003     content : {
17004         tag: 'tbody',
17005         cn: [
17006         {
17007             tag: 'tr',
17008             cn: [
17009             {
17010                 tag: 'td',
17011                 colspan: '7'
17012             }
17013             ]
17014         }
17015         ]
17016     },
17017     
17018     dates:{
17019         en: {
17020             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17021             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17022         }
17023     }
17024 });
17025
17026 Roo.apply(Roo.bootstrap.MonthField,  {
17027   
17028     template : {
17029         tag: 'div',
17030         cls: 'datepicker dropdown-menu roo-dynamic',
17031         cn: [
17032             {
17033                 tag: 'div',
17034                 cls: 'datepicker-months',
17035                 cn: [
17036                 {
17037                     tag: 'table',
17038                     cls: 'table-condensed',
17039                     cn:[
17040                         Roo.bootstrap.DateField.content
17041                     ]
17042                 }
17043                 ]
17044             }
17045         ]
17046     }
17047 });
17048
17049  
17050
17051  
17052  /*
17053  * - LGPL
17054  *
17055  * CheckBox
17056  * 
17057  */
17058
17059 /**
17060  * @class Roo.bootstrap.CheckBox
17061  * @extends Roo.bootstrap.Input
17062  * Bootstrap CheckBox class
17063  * 
17064  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17065  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17066  * @cfg {String} boxLabel The text that appears beside the checkbox
17067  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17068  * @cfg {Boolean} checked initnal the element
17069  * @cfg {Boolean} inline inline the element (default false)
17070  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17071  * 
17072  * @constructor
17073  * Create a new CheckBox
17074  * @param {Object} config The config object
17075  */
17076
17077 Roo.bootstrap.CheckBox = function(config){
17078     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17079    
17080     this.addEvents({
17081         /**
17082         * @event check
17083         * Fires when the element is checked or unchecked.
17084         * @param {Roo.bootstrap.CheckBox} this This input
17085         * @param {Boolean} checked The new checked value
17086         */
17087        check : true
17088     });
17089     
17090 };
17091
17092 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17093   
17094     inputType: 'checkbox',
17095     inputValue: 1,
17096     valueOff: 0,
17097     boxLabel: false,
17098     checked: false,
17099     weight : false,
17100     inline: false,
17101     
17102     getAutoCreate : function()
17103     {
17104         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17105         
17106         var id = Roo.id();
17107         
17108         var cfg = {};
17109         
17110         cfg.cls = 'form-group ' + this.inputType; //input-group
17111         
17112         if(this.inline){
17113             cfg.cls += ' ' + this.inputType + '-inline';
17114         }
17115         
17116         var input =  {
17117             tag: 'input',
17118             id : id,
17119             type : this.inputType,
17120             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17121             cls : 'roo-' + this.inputType, //'form-box',
17122             placeholder : this.placeholder || ''
17123             
17124         };
17125         
17126         if (this.weight) { // Validity check?
17127             cfg.cls += " " + this.inputType + "-" + this.weight;
17128         }
17129         
17130         if (this.disabled) {
17131             input.disabled=true;
17132         }
17133         
17134         if(this.checked){
17135             input.checked = this.checked;
17136         }
17137         
17138         if (this.name) {
17139             input.name = this.name;
17140         }
17141         
17142         if (this.size) {
17143             input.cls += ' input-' + this.size;
17144         }
17145         
17146         var settings=this;
17147         
17148         ['xs','sm','md','lg'].map(function(size){
17149             if (settings[size]) {
17150                 cfg.cls += ' col-' + size + '-' + settings[size];
17151             }
17152         });
17153         
17154         var inputblock = input;
17155          
17156         if (this.before || this.after) {
17157             
17158             inputblock = {
17159                 cls : 'input-group',
17160                 cn :  [] 
17161             };
17162             
17163             if (this.before) {
17164                 inputblock.cn.push({
17165                     tag :'span',
17166                     cls : 'input-group-addon',
17167                     html : this.before
17168                 });
17169             }
17170             
17171             inputblock.cn.push(input);
17172             
17173             if (this.after) {
17174                 inputblock.cn.push({
17175                     tag :'span',
17176                     cls : 'input-group-addon',
17177                     html : this.after
17178                 });
17179             }
17180             
17181         }
17182         
17183         if (align ==='left' && this.fieldLabel.length) {
17184                 Roo.log("left and has label");
17185                 cfg.cn = [
17186                     
17187                     {
17188                         tag: 'label',
17189                         'for' :  id,
17190                         cls : 'control-label col-md-' + this.labelWidth,
17191                         html : this.fieldLabel
17192                         
17193                     },
17194                     {
17195                         cls : "col-md-" + (12 - this.labelWidth), 
17196                         cn: [
17197                             inputblock
17198                         ]
17199                     }
17200                     
17201                 ];
17202         } else if ( this.fieldLabel.length) {
17203                 Roo.log(" label");
17204                 cfg.cn = [
17205                    
17206                     {
17207                         tag: this.boxLabel ? 'span' : 'label',
17208                         'for': id,
17209                         cls: 'control-label box-input-label',
17210                         //cls : 'input-group-addon',
17211                         html : this.fieldLabel
17212                         
17213                     },
17214                     
17215                     inputblock
17216                     
17217                 ];
17218
17219         } else {
17220             
17221                 Roo.log(" no label && no align");
17222                 cfg.cn = [  inputblock ] ;
17223                 
17224                 
17225         }
17226         if(this.boxLabel){
17227              var boxLabelCfg = {
17228                 tag: 'label',
17229                 //'for': id, // box label is handled by onclick - so no for...
17230                 cls: 'box-label',
17231                 html: this.boxLabel
17232             }
17233             
17234             if(this.tooltip){
17235                 boxLabelCfg.tooltip = this.tooltip;
17236             }
17237              
17238             cfg.cn.push(boxLabelCfg);
17239         }
17240         
17241         
17242        
17243         return cfg;
17244         
17245     },
17246     
17247     /**
17248      * return the real input element.
17249      */
17250     inputEl: function ()
17251     {
17252         return this.el.select('input.roo-' + this.inputType,true).first();
17253     },
17254     
17255     labelEl: function()
17256     {
17257         return this.el.select('label.control-label',true).first();
17258     },
17259     /* depricated... */
17260     
17261     label: function()
17262     {
17263         return this.labelEl();
17264     },
17265     
17266     initEvents : function()
17267     {
17268 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17269         
17270         this.inputEl().on('click', this.onClick,  this);
17271         
17272         if (this.boxLabel) { 
17273             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17274         }
17275         
17276         this.startValue = this.getValue();
17277         
17278         if(this.groupId){
17279             Roo.bootstrap.CheckBox.register(this);
17280         }
17281     },
17282     
17283     onClick : function()
17284     {   
17285         this.setChecked(!this.checked);
17286     },
17287     
17288     setChecked : function(state,suppressEvent)
17289     {
17290         this.startValue = this.getValue();
17291         
17292         if(this.inputType == 'radio'){
17293             
17294             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17295                 e.dom.checked = false;
17296             });
17297             
17298             this.inputEl().dom.checked = true;
17299             
17300             this.inputEl().dom.value = this.inputValue;
17301             
17302             if(suppressEvent !== true){
17303                 this.fireEvent('check', this, true);
17304             }
17305             
17306             this.validate();
17307             
17308             return;
17309         }
17310         
17311         this.checked = state;
17312         
17313         this.inputEl().dom.checked = state;
17314         
17315         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17316         
17317         if(suppressEvent !== true){
17318             this.fireEvent('check', this, state);
17319         }
17320         
17321         this.validate();
17322     },
17323     
17324     getValue : function()
17325     {
17326         if(this.inputType == 'radio'){
17327             return this.getGroupValue();
17328         }
17329         
17330         return this.inputEl().getValue();
17331         
17332     },
17333     
17334     getGroupValue : function()
17335     {
17336         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17337             return '';
17338         }
17339         
17340         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17341     },
17342     
17343     setValue : function(v,suppressEvent)
17344     {
17345         if(this.inputType == 'radio'){
17346             this.setGroupValue(v, suppressEvent);
17347             return;
17348         }
17349         
17350         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17351         
17352         this.validate();
17353     },
17354     
17355     setGroupValue : function(v, suppressEvent)
17356     {
17357         this.startValue = this.getValue();
17358         
17359         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17360             e.dom.checked = false;
17361             
17362             if(e.dom.value == v){
17363                 e.dom.checked = true;
17364             }
17365         });
17366         
17367         if(suppressEvent !== true){
17368             this.fireEvent('check', this, true);
17369         }
17370
17371         this.validate();
17372         
17373         return;
17374     },
17375     
17376     validate : function()
17377     {
17378         if(
17379                 this.disabled || 
17380                 (this.inputType == 'radio' && this.validateRadio()) ||
17381                 (this.inputType == 'checkbox' && this.validateCheckbox())
17382         ){
17383             this.markValid();
17384             return true;
17385         }
17386         
17387         this.markInvalid();
17388         return false;
17389     },
17390     
17391     validateRadio : function()
17392     {
17393         var valid = false;
17394         
17395         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17396             if(!e.dom.checked){
17397                 return;
17398             }
17399             
17400             valid = true;
17401             
17402             return false;
17403         });
17404         
17405         return valid;
17406     },
17407     
17408     validateCheckbox : function()
17409     {
17410         if(!this.groupId){
17411             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17412         }
17413         
17414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17415         
17416         if(!group){
17417             return false;
17418         }
17419         
17420         var r = false;
17421         
17422         for(var i in group){
17423             if(r){
17424                 break;
17425             }
17426             
17427             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17428         }
17429         
17430         return r;
17431     },
17432     
17433     /**
17434      * Mark this field as valid
17435      */
17436     markValid : function()
17437     {
17438         if(this.allowBlank){
17439             return;
17440         }
17441         
17442         var _this = this;
17443         
17444         this.fireEvent('valid', this);
17445         
17446         if(this.inputType == 'radio'){
17447             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17448                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17449                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17450             });
17451             
17452             return;
17453         }
17454         
17455         if(!this.groupId){
17456             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17457             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17458             return;
17459         }
17460         
17461         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17462             
17463         if(!group){
17464             return;
17465         }
17466         
17467         for(var i in group){
17468             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17469             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17470         }
17471     },
17472     
17473      /**
17474      * Mark this field as invalid
17475      * @param {String} msg The validation message
17476      */
17477     markInvalid : function(msg)
17478     {
17479         if(this.allowBlank){
17480             return;
17481         }
17482         
17483         var _this = this;
17484         
17485         this.fireEvent('invalid', this, msg);
17486         
17487         if(this.inputType == 'radio'){
17488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17489                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17490                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17491             });
17492             
17493             return;
17494         }
17495         
17496         if(!this.groupId){
17497             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17498             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17499             return;
17500         }
17501         
17502         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17503             
17504         if(!group){
17505             return;
17506         }
17507         
17508         for(var i in group){
17509             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17510             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17511         }
17512         
17513     }
17514     
17515 });
17516
17517 Roo.apply(Roo.bootstrap.CheckBox, {
17518     
17519     groups: {},
17520     
17521      /**
17522     * register a CheckBox Group
17523     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17524     */
17525     register : function(checkbox)
17526     {
17527         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17528             this.groups[checkbox.groupId] = {};
17529         }
17530         
17531         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17532             return;
17533         }
17534         
17535         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17536         
17537     },
17538     /**
17539     * fetch a CheckBox Group based on the group ID
17540     * @param {string} the group ID
17541     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17542     */
17543     get: function(groupId) {
17544         if (typeof(this.groups[groupId]) == 'undefined') {
17545             return false;
17546         }
17547         
17548         return this.groups[groupId] ;
17549     }
17550     
17551     
17552 });
17553 /*
17554  * - LGPL
17555  *
17556  * Radio
17557  *
17558  *
17559  * not inline
17560  *<div class="radio">
17561   <label>
17562     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17563     Option one is this and that&mdash;be sure to include why it's great
17564   </label>
17565 </div>
17566  *
17567  *
17568  *inline
17569  *<span>
17570  *<label class="radio-inline">fieldLabel</label>
17571  *<label class="radio-inline">
17572   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17573 </label>
17574 <span>
17575  * 
17576  * 
17577  */
17578
17579 /**
17580  * @class Roo.bootstrap.Radio
17581  * @extends Roo.bootstrap.CheckBox
17582  * Bootstrap Radio class
17583
17584  * @constructor
17585  * Create a new Radio
17586  * @param {Object} config The config object
17587  */
17588
17589 Roo.bootstrap.Radio = function(config){
17590     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17591    
17592 };
17593
17594 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17595     
17596     inputType: 'radio',
17597     inputValue: '',
17598     valueOff: '',
17599     
17600     getAutoCreate : function()
17601     {
17602         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17603         align = align || 'left'; // default...
17604         
17605         
17606         
17607         var id = Roo.id();
17608         
17609         var cfg = {
17610                 tag : this.inline ? 'span' : 'div',
17611                 cls : '',
17612                 cn : []
17613         };
17614         
17615         var inline = this.inline ? ' radio-inline' : '';
17616         
17617         var lbl = {
17618                 tag: 'label' ,
17619                 // does not need for, as we wrap the input with it..
17620                 'for' : id,
17621                 cls : 'control-label box-label' + inline,
17622                 cn : []
17623         };
17624         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17625         
17626         var fieldLabel = {
17627             tag: 'label' ,
17628             //cls : 'control-label' + inline,
17629             html : this.fieldLabel,
17630             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17631         };
17632         
17633  
17634         
17635         
17636         var input =  {
17637             tag: 'input',
17638             id : id,
17639             type : this.inputType,
17640             //value : (!this.checked) ? this.valueOff : this.inputValue,
17641             value : this.inputValue,
17642             cls : 'roo-radio',
17643             placeholder : this.placeholder || '' // ?? needed????
17644             
17645         };
17646         if (this.weight) { // Validity check?
17647             input.cls += " radio-" + this.weight;
17648         }
17649         if (this.disabled) {
17650             input.disabled=true;
17651         }
17652         
17653         if(this.checked){
17654             input.checked = this.checked;
17655         }
17656         
17657         if (this.name) {
17658             input.name = this.name;
17659         }
17660         
17661         if (this.size) {
17662             input.cls += ' input-' + this.size;
17663         }
17664         
17665         //?? can span's inline have a width??
17666         
17667         var settings=this;
17668         ['xs','sm','md','lg'].map(function(size){
17669             if (settings[size]) {
17670                 cfg.cls += ' col-' + size + '-' + settings[size];
17671             }
17672         });
17673         
17674         var inputblock = input;
17675         
17676         if (this.before || this.after) {
17677             
17678             inputblock = {
17679                 cls : 'input-group',
17680                 tag : 'span',
17681                 cn :  [] 
17682             };
17683             if (this.before) {
17684                 inputblock.cn.push({
17685                     tag :'span',
17686                     cls : 'input-group-addon',
17687                     html : this.before
17688                 });
17689             }
17690             inputblock.cn.push(input);
17691             if (this.after) {
17692                 inputblock.cn.push({
17693                     tag :'span',
17694                     cls : 'input-group-addon',
17695                     html : this.after
17696                 });
17697             }
17698             
17699         };
17700         
17701         
17702         if (this.fieldLabel && this.fieldLabel.length) {
17703             cfg.cn.push(fieldLabel);
17704         }
17705        
17706         // normal bootstrap puts the input inside the label.
17707         // however with our styled version - it has to go after the input.
17708        
17709         //lbl.cn.push(inputblock);
17710         
17711         var lblwrap =  {
17712             tag: 'span',
17713             cls: 'radio' + inline,
17714             cn: [
17715                 inputblock,
17716                 lbl
17717             ]
17718         };
17719         
17720         cfg.cn.push( lblwrap);
17721         
17722         if(this.boxLabel){
17723             lbl.cn.push({
17724                 tag: 'span',
17725                 html: this.boxLabel
17726             })
17727         }
17728          
17729         
17730         return cfg;
17731         
17732     },
17733     
17734     initEvents : function()
17735     {
17736 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17737         
17738         this.inputEl().on('click', this.onClick,  this);
17739         if (this.boxLabel) {
17740             Roo.log('find label')
17741             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17742         }
17743         
17744     },
17745     
17746     inputEl: function ()
17747     {
17748         return this.el.select('input.roo-radio',true).first();
17749     },
17750     onClick : function()
17751     {   
17752         Roo.log("click");
17753         this.setChecked(true);
17754     },
17755     
17756     setChecked : function(state,suppressEvent)
17757     {
17758         if(state){
17759             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17760                 v.dom.checked = false;
17761             });
17762         }
17763         Roo.log(this.inputEl().dom);
17764         this.checked = state;
17765         this.inputEl().dom.checked = state;
17766         
17767         if(suppressEvent !== true){
17768             this.fireEvent('check', this, state);
17769         }
17770         
17771         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17772         
17773     },
17774     
17775     getGroupValue : function()
17776     {
17777         var value = '';
17778         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17779             if(v.dom.checked == true){
17780                 value = v.dom.value;
17781             }
17782         });
17783         
17784         return value;
17785     },
17786     
17787     /**
17788      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17789      * @return {Mixed} value The field value
17790      */
17791     getValue : function(){
17792         return this.getGroupValue();
17793     }
17794     
17795 });
17796
17797  
17798 //<script type="text/javascript">
17799
17800 /*
17801  * Based  Ext JS Library 1.1.1
17802  * Copyright(c) 2006-2007, Ext JS, LLC.
17803  * LGPL
17804  *
17805  */
17806  
17807 /**
17808  * @class Roo.HtmlEditorCore
17809  * @extends Roo.Component
17810  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17811  *
17812  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17813  */
17814
17815 Roo.HtmlEditorCore = function(config){
17816     
17817     
17818     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17819     
17820     
17821     this.addEvents({
17822         /**
17823          * @event initialize
17824          * Fires when the editor is fully initialized (including the iframe)
17825          * @param {Roo.HtmlEditorCore} this
17826          */
17827         initialize: true,
17828         /**
17829          * @event activate
17830          * Fires when the editor is first receives the focus. Any insertion must wait
17831          * until after this event.
17832          * @param {Roo.HtmlEditorCore} this
17833          */
17834         activate: true,
17835          /**
17836          * @event beforesync
17837          * Fires before the textarea is updated with content from the editor iframe. Return false
17838          * to cancel the sync.
17839          * @param {Roo.HtmlEditorCore} this
17840          * @param {String} html
17841          */
17842         beforesync: true,
17843          /**
17844          * @event beforepush
17845          * Fires before the iframe editor is updated with content from the textarea. Return false
17846          * to cancel the push.
17847          * @param {Roo.HtmlEditorCore} this
17848          * @param {String} html
17849          */
17850         beforepush: true,
17851          /**
17852          * @event sync
17853          * Fires when the textarea is updated with content from the editor iframe.
17854          * @param {Roo.HtmlEditorCore} this
17855          * @param {String} html
17856          */
17857         sync: true,
17858          /**
17859          * @event push
17860          * Fires when the iframe editor is updated with content from the textarea.
17861          * @param {Roo.HtmlEditorCore} this
17862          * @param {String} html
17863          */
17864         push: true,
17865         
17866         /**
17867          * @event editorevent
17868          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17869          * @param {Roo.HtmlEditorCore} this
17870          */
17871         editorevent: true
17872         
17873     });
17874     
17875     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17876     
17877     // defaults : white / black...
17878     this.applyBlacklists();
17879     
17880     
17881     
17882 };
17883
17884
17885 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17886
17887
17888      /**
17889      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17890      */
17891     
17892     owner : false,
17893     
17894      /**
17895      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17896      *                        Roo.resizable.
17897      */
17898     resizable : false,
17899      /**
17900      * @cfg {Number} height (in pixels)
17901      */   
17902     height: 300,
17903    /**
17904      * @cfg {Number} width (in pixels)
17905      */   
17906     width: 500,
17907     
17908     /**
17909      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17910      * 
17911      */
17912     stylesheets: false,
17913     
17914     // id of frame..
17915     frameId: false,
17916     
17917     // private properties
17918     validationEvent : false,
17919     deferHeight: true,
17920     initialized : false,
17921     activated : false,
17922     sourceEditMode : false,
17923     onFocus : Roo.emptyFn,
17924     iframePad:3,
17925     hideMode:'offsets',
17926     
17927     clearUp: true,
17928     
17929     // blacklist + whitelisted elements..
17930     black: false,
17931     white: false,
17932      
17933     
17934
17935     /**
17936      * Protected method that will not generally be called directly. It
17937      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17938      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17939      */
17940     getDocMarkup : function(){
17941         // body styles..
17942         var st = '';
17943         
17944         // inherit styels from page...?? 
17945         if (this.stylesheets === false) {
17946             
17947             Roo.get(document.head).select('style').each(function(node) {
17948                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17949             });
17950             
17951             Roo.get(document.head).select('link').each(function(node) { 
17952                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17953             });
17954             
17955         } else if (!this.stylesheets.length) {
17956                 // simple..
17957                 st = '<style type="text/css">' +
17958                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17959                    '</style>';
17960         } else { 
17961             
17962         }
17963         
17964         st +=  '<style type="text/css">' +
17965             'IMG { cursor: pointer } ' +
17966         '</style>';
17967
17968         
17969         return '<html><head>' + st  +
17970             //<style type="text/css">' +
17971             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17972             //'</style>' +
17973             ' </head><body class="roo-htmleditor-body"></body></html>';
17974     },
17975
17976     // private
17977     onRender : function(ct, position)
17978     {
17979         var _t = this;
17980         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17981         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17982         
17983         
17984         this.el.dom.style.border = '0 none';
17985         this.el.dom.setAttribute('tabIndex', -1);
17986         this.el.addClass('x-hidden hide');
17987         
17988         
17989         
17990         if(Roo.isIE){ // fix IE 1px bogus margin
17991             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17992         }
17993        
17994         
17995         this.frameId = Roo.id();
17996         
17997          
17998         
17999         var iframe = this.owner.wrap.createChild({
18000             tag: 'iframe',
18001             cls: 'form-control', // bootstrap..
18002             id: this.frameId,
18003             name: this.frameId,
18004             frameBorder : 'no',
18005             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18006         }, this.el
18007         );
18008         
18009         
18010         this.iframe = iframe.dom;
18011
18012          this.assignDocWin();
18013         
18014         this.doc.designMode = 'on';
18015        
18016         this.doc.open();
18017         this.doc.write(this.getDocMarkup());
18018         this.doc.close();
18019
18020         
18021         var task = { // must defer to wait for browser to be ready
18022             run : function(){
18023                 //console.log("run task?" + this.doc.readyState);
18024                 this.assignDocWin();
18025                 if(this.doc.body || this.doc.readyState == 'complete'){
18026                     try {
18027                         this.doc.designMode="on";
18028                     } catch (e) {
18029                         return;
18030                     }
18031                     Roo.TaskMgr.stop(task);
18032                     this.initEditor.defer(10, this);
18033                 }
18034             },
18035             interval : 10,
18036             duration: 10000,
18037             scope: this
18038         };
18039         Roo.TaskMgr.start(task);
18040
18041     },
18042
18043     // private
18044     onResize : function(w, h)
18045     {
18046          Roo.log('resize: ' +w + ',' + h );
18047         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18048         if(!this.iframe){
18049             return;
18050         }
18051         if(typeof w == 'number'){
18052             
18053             this.iframe.style.width = w + 'px';
18054         }
18055         if(typeof h == 'number'){
18056             
18057             this.iframe.style.height = h + 'px';
18058             if(this.doc){
18059                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18060             }
18061         }
18062         
18063     },
18064
18065     /**
18066      * Toggles the editor between standard and source edit mode.
18067      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18068      */
18069     toggleSourceEdit : function(sourceEditMode){
18070         
18071         this.sourceEditMode = sourceEditMode === true;
18072         
18073         if(this.sourceEditMode){
18074  
18075             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18076             
18077         }else{
18078             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18079             //this.iframe.className = '';
18080             this.deferFocus();
18081         }
18082         //this.setSize(this.owner.wrap.getSize());
18083         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18084     },
18085
18086     
18087   
18088
18089     /**
18090      * Protected method that will not generally be called directly. If you need/want
18091      * custom HTML cleanup, this is the method you should override.
18092      * @param {String} html The HTML to be cleaned
18093      * return {String} The cleaned HTML
18094      */
18095     cleanHtml : function(html){
18096         html = String(html);
18097         if(html.length > 5){
18098             if(Roo.isSafari){ // strip safari nonsense
18099                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18100             }
18101         }
18102         if(html == '&nbsp;'){
18103             html = '';
18104         }
18105         return html;
18106     },
18107
18108     /**
18109      * HTML Editor -> Textarea
18110      * Protected method that will not generally be called directly. Syncs the contents
18111      * of the editor iframe with the textarea.
18112      */
18113     syncValue : function(){
18114         if(this.initialized){
18115             var bd = (this.doc.body || this.doc.documentElement);
18116             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18117             var html = bd.innerHTML;
18118             if(Roo.isSafari){
18119                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18120                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18121                 if(m && m[1]){
18122                     html = '<div style="'+m[0]+'">' + html + '</div>';
18123                 }
18124             }
18125             html = this.cleanHtml(html);
18126             // fix up the special chars.. normaly like back quotes in word...
18127             // however we do not want to do this with chinese..
18128             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18129                 var cc = b.charCodeAt();
18130                 if (
18131                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18132                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18133                     (cc >= 0xf900 && cc < 0xfb00 )
18134                 ) {
18135                         return b;
18136                 }
18137                 return "&#"+cc+";" 
18138             });
18139             if(this.owner.fireEvent('beforesync', this, html) !== false){
18140                 this.el.dom.value = html;
18141                 this.owner.fireEvent('sync', this, html);
18142             }
18143         }
18144     },
18145
18146     /**
18147      * Protected method that will not generally be called directly. Pushes the value of the textarea
18148      * into the iframe editor.
18149      */
18150     pushValue : function(){
18151         if(this.initialized){
18152             var v = this.el.dom.value.trim();
18153             
18154 //            if(v.length < 1){
18155 //                v = '&#160;';
18156 //            }
18157             
18158             if(this.owner.fireEvent('beforepush', this, v) !== false){
18159                 var d = (this.doc.body || this.doc.documentElement);
18160                 d.innerHTML = v;
18161                 this.cleanUpPaste();
18162                 this.el.dom.value = d.innerHTML;
18163                 this.owner.fireEvent('push', this, v);
18164             }
18165         }
18166     },
18167
18168     // private
18169     deferFocus : function(){
18170         this.focus.defer(10, this);
18171     },
18172
18173     // doc'ed in Field
18174     focus : function(){
18175         if(this.win && !this.sourceEditMode){
18176             this.win.focus();
18177         }else{
18178             this.el.focus();
18179         }
18180     },
18181     
18182     assignDocWin: function()
18183     {
18184         var iframe = this.iframe;
18185         
18186          if(Roo.isIE){
18187             this.doc = iframe.contentWindow.document;
18188             this.win = iframe.contentWindow;
18189         } else {
18190 //            if (!Roo.get(this.frameId)) {
18191 //                return;
18192 //            }
18193 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18194 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18195             
18196             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18197                 return;
18198             }
18199             
18200             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18201             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18202         }
18203     },
18204     
18205     // private
18206     initEditor : function(){
18207         //console.log("INIT EDITOR");
18208         this.assignDocWin();
18209         
18210         
18211         
18212         this.doc.designMode="on";
18213         this.doc.open();
18214         this.doc.write(this.getDocMarkup());
18215         this.doc.close();
18216         
18217         var dbody = (this.doc.body || this.doc.documentElement);
18218         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18219         // this copies styles from the containing element into thsi one..
18220         // not sure why we need all of this..
18221         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18222         
18223         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18224         //ss['background-attachment'] = 'fixed'; // w3c
18225         dbody.bgProperties = 'fixed'; // ie
18226         //Roo.DomHelper.applyStyles(dbody, ss);
18227         Roo.EventManager.on(this.doc, {
18228             //'mousedown': this.onEditorEvent,
18229             'mouseup': this.onEditorEvent,
18230             'dblclick': this.onEditorEvent,
18231             'click': this.onEditorEvent,
18232             'keyup': this.onEditorEvent,
18233             buffer:100,
18234             scope: this
18235         });
18236         if(Roo.isGecko){
18237             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18238         }
18239         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18240             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18241         }
18242         this.initialized = true;
18243
18244         this.owner.fireEvent('initialize', this);
18245         this.pushValue();
18246     },
18247
18248     // private
18249     onDestroy : function(){
18250         
18251         
18252         
18253         if(this.rendered){
18254             
18255             //for (var i =0; i < this.toolbars.length;i++) {
18256             //    // fixme - ask toolbars for heights?
18257             //    this.toolbars[i].onDestroy();
18258            // }
18259             
18260             //this.wrap.dom.innerHTML = '';
18261             //this.wrap.remove();
18262         }
18263     },
18264
18265     // private
18266     onFirstFocus : function(){
18267         
18268         this.assignDocWin();
18269         
18270         
18271         this.activated = true;
18272          
18273     
18274         if(Roo.isGecko){ // prevent silly gecko errors
18275             this.win.focus();
18276             var s = this.win.getSelection();
18277             if(!s.focusNode || s.focusNode.nodeType != 3){
18278                 var r = s.getRangeAt(0);
18279                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18280                 r.collapse(true);
18281                 this.deferFocus();
18282             }
18283             try{
18284                 this.execCmd('useCSS', true);
18285                 this.execCmd('styleWithCSS', false);
18286             }catch(e){}
18287         }
18288         this.owner.fireEvent('activate', this);
18289     },
18290
18291     // private
18292     adjustFont: function(btn){
18293         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18294         //if(Roo.isSafari){ // safari
18295         //    adjust *= 2;
18296        // }
18297         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18298         if(Roo.isSafari){ // safari
18299             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18300             v =  (v < 10) ? 10 : v;
18301             v =  (v > 48) ? 48 : v;
18302             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18303             
18304         }
18305         
18306         
18307         v = Math.max(1, v+adjust);
18308         
18309         this.execCmd('FontSize', v  );
18310     },
18311
18312     onEditorEvent : function(e){
18313         this.owner.fireEvent('editorevent', this, e);
18314       //  this.updateToolbar();
18315         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18316     },
18317
18318     insertTag : function(tg)
18319     {
18320         // could be a bit smarter... -> wrap the current selected tRoo..
18321         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18322             
18323             range = this.createRange(this.getSelection());
18324             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18325             wrappingNode.appendChild(range.extractContents());
18326             range.insertNode(wrappingNode);
18327
18328             return;
18329             
18330             
18331             
18332         }
18333         this.execCmd("formatblock",   tg);
18334         
18335     },
18336     
18337     insertText : function(txt)
18338     {
18339         
18340         
18341         var range = this.createRange();
18342         range.deleteContents();
18343                //alert(Sender.getAttribute('label'));
18344                
18345         range.insertNode(this.doc.createTextNode(txt));
18346     } ,
18347     
18348      
18349
18350     /**
18351      * Executes a Midas editor command on the editor document and performs necessary focus and
18352      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18353      * @param {String} cmd The Midas command
18354      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18355      */
18356     relayCmd : function(cmd, value){
18357         this.win.focus();
18358         this.execCmd(cmd, value);
18359         this.owner.fireEvent('editorevent', this);
18360         //this.updateToolbar();
18361         this.owner.deferFocus();
18362     },
18363
18364     /**
18365      * Executes a Midas editor command directly on the editor document.
18366      * For visual commands, you should use {@link #relayCmd} instead.
18367      * <b>This should only be called after the editor is initialized.</b>
18368      * @param {String} cmd The Midas command
18369      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18370      */
18371     execCmd : function(cmd, value){
18372         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18373         this.syncValue();
18374     },
18375  
18376  
18377    
18378     /**
18379      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18380      * to insert tRoo.
18381      * @param {String} text | dom node.. 
18382      */
18383     insertAtCursor : function(text)
18384     {
18385         
18386         
18387         
18388         if(!this.activated){
18389             return;
18390         }
18391         /*
18392         if(Roo.isIE){
18393             this.win.focus();
18394             var r = this.doc.selection.createRange();
18395             if(r){
18396                 r.collapse(true);
18397                 r.pasteHTML(text);
18398                 this.syncValue();
18399                 this.deferFocus();
18400             
18401             }
18402             return;
18403         }
18404         */
18405         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18406             this.win.focus();
18407             
18408             
18409             // from jquery ui (MIT licenced)
18410             var range, node;
18411             var win = this.win;
18412             
18413             if (win.getSelection && win.getSelection().getRangeAt) {
18414                 range = win.getSelection().getRangeAt(0);
18415                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18416                 range.insertNode(node);
18417             } else if (win.document.selection && win.document.selection.createRange) {
18418                 // no firefox support
18419                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18420                 win.document.selection.createRange().pasteHTML(txt);
18421             } else {
18422                 // no firefox support
18423                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18424                 this.execCmd('InsertHTML', txt);
18425             } 
18426             
18427             this.syncValue();
18428             
18429             this.deferFocus();
18430         }
18431     },
18432  // private
18433     mozKeyPress : function(e){
18434         if(e.ctrlKey){
18435             var c = e.getCharCode(), cmd;
18436           
18437             if(c > 0){
18438                 c = String.fromCharCode(c).toLowerCase();
18439                 switch(c){
18440                     case 'b':
18441                         cmd = 'bold';
18442                         break;
18443                     case 'i':
18444                         cmd = 'italic';
18445                         break;
18446                     
18447                     case 'u':
18448                         cmd = 'underline';
18449                         break;
18450                     
18451                     case 'v':
18452                         this.cleanUpPaste.defer(100, this);
18453                         return;
18454                         
18455                 }
18456                 if(cmd){
18457                     this.win.focus();
18458                     this.execCmd(cmd);
18459                     this.deferFocus();
18460                     e.preventDefault();
18461                 }
18462                 
18463             }
18464         }
18465     },
18466
18467     // private
18468     fixKeys : function(){ // load time branching for fastest keydown performance
18469         if(Roo.isIE){
18470             return function(e){
18471                 var k = e.getKey(), r;
18472                 if(k == e.TAB){
18473                     e.stopEvent();
18474                     r = this.doc.selection.createRange();
18475                     if(r){
18476                         r.collapse(true);
18477                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18478                         this.deferFocus();
18479                     }
18480                     return;
18481                 }
18482                 
18483                 if(k == e.ENTER){
18484                     r = this.doc.selection.createRange();
18485                     if(r){
18486                         var target = r.parentElement();
18487                         if(!target || target.tagName.toLowerCase() != 'li'){
18488                             e.stopEvent();
18489                             r.pasteHTML('<br />');
18490                             r.collapse(false);
18491                             r.select();
18492                         }
18493                     }
18494                 }
18495                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18496                     this.cleanUpPaste.defer(100, this);
18497                     return;
18498                 }
18499                 
18500                 
18501             };
18502         }else if(Roo.isOpera){
18503             return function(e){
18504                 var k = e.getKey();
18505                 if(k == e.TAB){
18506                     e.stopEvent();
18507                     this.win.focus();
18508                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18509                     this.deferFocus();
18510                 }
18511                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18512                     this.cleanUpPaste.defer(100, this);
18513                     return;
18514                 }
18515                 
18516             };
18517         }else if(Roo.isSafari){
18518             return function(e){
18519                 var k = e.getKey();
18520                 
18521                 if(k == e.TAB){
18522                     e.stopEvent();
18523                     this.execCmd('InsertText','\t');
18524                     this.deferFocus();
18525                     return;
18526                 }
18527                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18528                     this.cleanUpPaste.defer(100, this);
18529                     return;
18530                 }
18531                 
18532              };
18533         }
18534     }(),
18535     
18536     getAllAncestors: function()
18537     {
18538         var p = this.getSelectedNode();
18539         var a = [];
18540         if (!p) {
18541             a.push(p); // push blank onto stack..
18542             p = this.getParentElement();
18543         }
18544         
18545         
18546         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18547             a.push(p);
18548             p = p.parentNode;
18549         }
18550         a.push(this.doc.body);
18551         return a;
18552     },
18553     lastSel : false,
18554     lastSelNode : false,
18555     
18556     
18557     getSelection : function() 
18558     {
18559         this.assignDocWin();
18560         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18561     },
18562     
18563     getSelectedNode: function() 
18564     {
18565         // this may only work on Gecko!!!
18566         
18567         // should we cache this!!!!
18568         
18569         
18570         
18571          
18572         var range = this.createRange(this.getSelection()).cloneRange();
18573         
18574         if (Roo.isIE) {
18575             var parent = range.parentElement();
18576             while (true) {
18577                 var testRange = range.duplicate();
18578                 testRange.moveToElementText(parent);
18579                 if (testRange.inRange(range)) {
18580                     break;
18581                 }
18582                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18583                     break;
18584                 }
18585                 parent = parent.parentElement;
18586             }
18587             return parent;
18588         }
18589         
18590         // is ancestor a text element.
18591         var ac =  range.commonAncestorContainer;
18592         if (ac.nodeType == 3) {
18593             ac = ac.parentNode;
18594         }
18595         
18596         var ar = ac.childNodes;
18597          
18598         var nodes = [];
18599         var other_nodes = [];
18600         var has_other_nodes = false;
18601         for (var i=0;i<ar.length;i++) {
18602             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18603                 continue;
18604             }
18605             // fullly contained node.
18606             
18607             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18608                 nodes.push(ar[i]);
18609                 continue;
18610             }
18611             
18612             // probably selected..
18613             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18614                 other_nodes.push(ar[i]);
18615                 continue;
18616             }
18617             // outer..
18618             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18619                 continue;
18620             }
18621             
18622             
18623             has_other_nodes = true;
18624         }
18625         if (!nodes.length && other_nodes.length) {
18626             nodes= other_nodes;
18627         }
18628         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18629             return false;
18630         }
18631         
18632         return nodes[0];
18633     },
18634     createRange: function(sel)
18635     {
18636         // this has strange effects when using with 
18637         // top toolbar - not sure if it's a great idea.
18638         //this.editor.contentWindow.focus();
18639         if (typeof sel != "undefined") {
18640             try {
18641                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18642             } catch(e) {
18643                 return this.doc.createRange();
18644             }
18645         } else {
18646             return this.doc.createRange();
18647         }
18648     },
18649     getParentElement: function()
18650     {
18651         
18652         this.assignDocWin();
18653         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18654         
18655         var range = this.createRange(sel);
18656          
18657         try {
18658             var p = range.commonAncestorContainer;
18659             while (p.nodeType == 3) { // text node
18660                 p = p.parentNode;
18661             }
18662             return p;
18663         } catch (e) {
18664             return null;
18665         }
18666     
18667     },
18668     /***
18669      *
18670      * Range intersection.. the hard stuff...
18671      *  '-1' = before
18672      *  '0' = hits..
18673      *  '1' = after.
18674      *         [ -- selected range --- ]
18675      *   [fail]                        [fail]
18676      *
18677      *    basically..
18678      *      if end is before start or  hits it. fail.
18679      *      if start is after end or hits it fail.
18680      *
18681      *   if either hits (but other is outside. - then it's not 
18682      *   
18683      *    
18684      **/
18685     
18686     
18687     // @see http://www.thismuchiknow.co.uk/?p=64.
18688     rangeIntersectsNode : function(range, node)
18689     {
18690         var nodeRange = node.ownerDocument.createRange();
18691         try {
18692             nodeRange.selectNode(node);
18693         } catch (e) {
18694             nodeRange.selectNodeContents(node);
18695         }
18696     
18697         var rangeStartRange = range.cloneRange();
18698         rangeStartRange.collapse(true);
18699     
18700         var rangeEndRange = range.cloneRange();
18701         rangeEndRange.collapse(false);
18702     
18703         var nodeStartRange = nodeRange.cloneRange();
18704         nodeStartRange.collapse(true);
18705     
18706         var nodeEndRange = nodeRange.cloneRange();
18707         nodeEndRange.collapse(false);
18708     
18709         return rangeStartRange.compareBoundaryPoints(
18710                  Range.START_TO_START, nodeEndRange) == -1 &&
18711                rangeEndRange.compareBoundaryPoints(
18712                  Range.START_TO_START, nodeStartRange) == 1;
18713         
18714          
18715     },
18716     rangeCompareNode : function(range, node)
18717     {
18718         var nodeRange = node.ownerDocument.createRange();
18719         try {
18720             nodeRange.selectNode(node);
18721         } catch (e) {
18722             nodeRange.selectNodeContents(node);
18723         }
18724         
18725         
18726         range.collapse(true);
18727     
18728         nodeRange.collapse(true);
18729      
18730         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18731         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18732          
18733         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18734         
18735         var nodeIsBefore   =  ss == 1;
18736         var nodeIsAfter    = ee == -1;
18737         
18738         if (nodeIsBefore && nodeIsAfter)
18739             return 0; // outer
18740         if (!nodeIsBefore && nodeIsAfter)
18741             return 1; //right trailed.
18742         
18743         if (nodeIsBefore && !nodeIsAfter)
18744             return 2;  // left trailed.
18745         // fully contined.
18746         return 3;
18747     },
18748
18749     // private? - in a new class?
18750     cleanUpPaste :  function()
18751     {
18752         // cleans up the whole document..
18753         Roo.log('cleanuppaste');
18754         
18755         this.cleanUpChildren(this.doc.body);
18756         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18757         if (clean != this.doc.body.innerHTML) {
18758             this.doc.body.innerHTML = clean;
18759         }
18760         
18761     },
18762     
18763     cleanWordChars : function(input) {// change the chars to hex code
18764         var he = Roo.HtmlEditorCore;
18765         
18766         var output = input;
18767         Roo.each(he.swapCodes, function(sw) { 
18768             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18769             
18770             output = output.replace(swapper, sw[1]);
18771         });
18772         
18773         return output;
18774     },
18775     
18776     
18777     cleanUpChildren : function (n)
18778     {
18779         if (!n.childNodes.length) {
18780             return;
18781         }
18782         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18783            this.cleanUpChild(n.childNodes[i]);
18784         }
18785     },
18786     
18787     
18788         
18789     
18790     cleanUpChild : function (node)
18791     {
18792         var ed = this;
18793         //console.log(node);
18794         if (node.nodeName == "#text") {
18795             // clean up silly Windows -- stuff?
18796             return; 
18797         }
18798         if (node.nodeName == "#comment") {
18799             node.parentNode.removeChild(node);
18800             // clean up silly Windows -- stuff?
18801             return; 
18802         }
18803         var lcname = node.tagName.toLowerCase();
18804         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18805         // whitelist of tags..
18806         
18807         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18808             // remove node.
18809             node.parentNode.removeChild(node);
18810             return;
18811             
18812         }
18813         
18814         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18815         
18816         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18817         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18818         
18819         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18820         //    remove_keep_children = true;
18821         //}
18822         
18823         if (remove_keep_children) {
18824             this.cleanUpChildren(node);
18825             // inserts everything just before this node...
18826             while (node.childNodes.length) {
18827                 var cn = node.childNodes[0];
18828                 node.removeChild(cn);
18829                 node.parentNode.insertBefore(cn, node);
18830             }
18831             node.parentNode.removeChild(node);
18832             return;
18833         }
18834         
18835         if (!node.attributes || !node.attributes.length) {
18836             this.cleanUpChildren(node);
18837             return;
18838         }
18839         
18840         function cleanAttr(n,v)
18841         {
18842             
18843             if (v.match(/^\./) || v.match(/^\//)) {
18844                 return;
18845             }
18846             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18847                 return;
18848             }
18849             if (v.match(/^#/)) {
18850                 return;
18851             }
18852 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18853             node.removeAttribute(n);
18854             
18855         }
18856         
18857         var cwhite = this.cwhite;
18858         var cblack = this.cblack;
18859             
18860         function cleanStyle(n,v)
18861         {
18862             if (v.match(/expression/)) { //XSS?? should we even bother..
18863                 node.removeAttribute(n);
18864                 return;
18865             }
18866             
18867             var parts = v.split(/;/);
18868             var clean = [];
18869             
18870             Roo.each(parts, function(p) {
18871                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18872                 if (!p.length) {
18873                     return true;
18874                 }
18875                 var l = p.split(':').shift().replace(/\s+/g,'');
18876                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18877                 
18878                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18879 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18880                     //node.removeAttribute(n);
18881                     return true;
18882                 }
18883                 //Roo.log()
18884                 // only allow 'c whitelisted system attributes'
18885                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18886 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18887                     //node.removeAttribute(n);
18888                     return true;
18889                 }
18890                 
18891                 
18892                  
18893                 
18894                 clean.push(p);
18895                 return true;
18896             });
18897             if (clean.length) { 
18898                 node.setAttribute(n, clean.join(';'));
18899             } else {
18900                 node.removeAttribute(n);
18901             }
18902             
18903         }
18904         
18905         
18906         for (var i = node.attributes.length-1; i > -1 ; i--) {
18907             var a = node.attributes[i];
18908             //console.log(a);
18909             
18910             if (a.name.toLowerCase().substr(0,2)=='on')  {
18911                 node.removeAttribute(a.name);
18912                 continue;
18913             }
18914             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18915                 node.removeAttribute(a.name);
18916                 continue;
18917             }
18918             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18919                 cleanAttr(a.name,a.value); // fixme..
18920                 continue;
18921             }
18922             if (a.name == 'style') {
18923                 cleanStyle(a.name,a.value);
18924                 continue;
18925             }
18926             /// clean up MS crap..
18927             // tecnically this should be a list of valid class'es..
18928             
18929             
18930             if (a.name == 'class') {
18931                 if (a.value.match(/^Mso/)) {
18932                     node.className = '';
18933                 }
18934                 
18935                 if (a.value.match(/body/)) {
18936                     node.className = '';
18937                 }
18938                 continue;
18939             }
18940             
18941             // style cleanup!?
18942             // class cleanup?
18943             
18944         }
18945         
18946         
18947         this.cleanUpChildren(node);
18948         
18949         
18950     },
18951     /**
18952      * Clean up MS wordisms...
18953      */
18954     cleanWord : function(node)
18955     {
18956         var _t = this;
18957         var cleanWordChildren = function()
18958         {
18959             if (!node.childNodes.length) {
18960                 return;
18961             }
18962             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18963                _t.cleanWord(node.childNodes[i]);
18964             }
18965         }
18966         
18967         
18968         if (!node) {
18969             this.cleanWord(this.doc.body);
18970             return;
18971         }
18972         if (node.nodeName == "#text") {
18973             // clean up silly Windows -- stuff?
18974             return; 
18975         }
18976         if (node.nodeName == "#comment") {
18977             node.parentNode.removeChild(node);
18978             // clean up silly Windows -- stuff?
18979             return; 
18980         }
18981         
18982         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18983             node.parentNode.removeChild(node);
18984             return;
18985         }
18986         
18987         // remove - but keep children..
18988         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18989             while (node.childNodes.length) {
18990                 var cn = node.childNodes[0];
18991                 node.removeChild(cn);
18992                 node.parentNode.insertBefore(cn, node);
18993             }
18994             node.parentNode.removeChild(node);
18995             cleanWordChildren();
18996             return;
18997         }
18998         // clean styles
18999         if (node.className.length) {
19000             
19001             var cn = node.className.split(/\W+/);
19002             var cna = [];
19003             Roo.each(cn, function(cls) {
19004                 if (cls.match(/Mso[a-zA-Z]+/)) {
19005                     return;
19006                 }
19007                 cna.push(cls);
19008             });
19009             node.className = cna.length ? cna.join(' ') : '';
19010             if (!cna.length) {
19011                 node.removeAttribute("class");
19012             }
19013         }
19014         
19015         if (node.hasAttribute("lang")) {
19016             node.removeAttribute("lang");
19017         }
19018         
19019         if (node.hasAttribute("style")) {
19020             
19021             var styles = node.getAttribute("style").split(";");
19022             var nstyle = [];
19023             Roo.each(styles, function(s) {
19024                 if (!s.match(/:/)) {
19025                     return;
19026                 }
19027                 var kv = s.split(":");
19028                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19029                     return;
19030                 }
19031                 // what ever is left... we allow.
19032                 nstyle.push(s);
19033             });
19034             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19035             if (!nstyle.length) {
19036                 node.removeAttribute('style');
19037             }
19038         }
19039         
19040         cleanWordChildren();
19041         
19042         
19043     },
19044     domToHTML : function(currentElement, depth, nopadtext) {
19045         
19046         depth = depth || 0;
19047         nopadtext = nopadtext || false;
19048     
19049         if (!currentElement) {
19050             return this.domToHTML(this.doc.body);
19051         }
19052         
19053         //Roo.log(currentElement);
19054         var j;
19055         var allText = false;
19056         var nodeName = currentElement.nodeName;
19057         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19058         
19059         if  (nodeName == '#text') {
19060             
19061             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19062         }
19063         
19064         
19065         var ret = '';
19066         if (nodeName != 'BODY') {
19067              
19068             var i = 0;
19069             // Prints the node tagName, such as <A>, <IMG>, etc
19070             if (tagName) {
19071                 var attr = [];
19072                 for(i = 0; i < currentElement.attributes.length;i++) {
19073                     // quoting?
19074                     var aname = currentElement.attributes.item(i).name;
19075                     if (!currentElement.attributes.item(i).value.length) {
19076                         continue;
19077                     }
19078                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19079                 }
19080                 
19081                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19082             } 
19083             else {
19084                 
19085                 // eack
19086             }
19087         } else {
19088             tagName = false;
19089         }
19090         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19091             return ret;
19092         }
19093         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19094             nopadtext = true;
19095         }
19096         
19097         
19098         // Traverse the tree
19099         i = 0;
19100         var currentElementChild = currentElement.childNodes.item(i);
19101         var allText = true;
19102         var innerHTML  = '';
19103         lastnode = '';
19104         while (currentElementChild) {
19105             // Formatting code (indent the tree so it looks nice on the screen)
19106             var nopad = nopadtext;
19107             if (lastnode == 'SPAN') {
19108                 nopad  = true;
19109             }
19110             // text
19111             if  (currentElementChild.nodeName == '#text') {
19112                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19113                 toadd = nopadtext ? toadd : toadd.trim();
19114                 if (!nopad && toadd.length > 80) {
19115                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19116                 }
19117                 innerHTML  += toadd;
19118                 
19119                 i++;
19120                 currentElementChild = currentElement.childNodes.item(i);
19121                 lastNode = '';
19122                 continue;
19123             }
19124             allText = false;
19125             
19126             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19127                 
19128             // Recursively traverse the tree structure of the child node
19129             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19130             lastnode = currentElementChild.nodeName;
19131             i++;
19132             currentElementChild=currentElement.childNodes.item(i);
19133         }
19134         
19135         ret += innerHTML;
19136         
19137         if (!allText) {
19138                 // The remaining code is mostly for formatting the tree
19139             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19140         }
19141         
19142         
19143         if (tagName) {
19144             ret+= "</"+tagName+">";
19145         }
19146         return ret;
19147         
19148     },
19149         
19150     applyBlacklists : function()
19151     {
19152         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19153         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19154         
19155         this.white = [];
19156         this.black = [];
19157         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19158             if (b.indexOf(tag) > -1) {
19159                 return;
19160             }
19161             this.white.push(tag);
19162             
19163         }, this);
19164         
19165         Roo.each(w, function(tag) {
19166             if (b.indexOf(tag) > -1) {
19167                 return;
19168             }
19169             if (this.white.indexOf(tag) > -1) {
19170                 return;
19171             }
19172             this.white.push(tag);
19173             
19174         }, this);
19175         
19176         
19177         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19178             if (w.indexOf(tag) > -1) {
19179                 return;
19180             }
19181             this.black.push(tag);
19182             
19183         }, this);
19184         
19185         Roo.each(b, function(tag) {
19186             if (w.indexOf(tag) > -1) {
19187                 return;
19188             }
19189             if (this.black.indexOf(tag) > -1) {
19190                 return;
19191             }
19192             this.black.push(tag);
19193             
19194         }, this);
19195         
19196         
19197         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19198         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19199         
19200         this.cwhite = [];
19201         this.cblack = [];
19202         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19203             if (b.indexOf(tag) > -1) {
19204                 return;
19205             }
19206             this.cwhite.push(tag);
19207             
19208         }, this);
19209         
19210         Roo.each(w, function(tag) {
19211             if (b.indexOf(tag) > -1) {
19212                 return;
19213             }
19214             if (this.cwhite.indexOf(tag) > -1) {
19215                 return;
19216             }
19217             this.cwhite.push(tag);
19218             
19219         }, this);
19220         
19221         
19222         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19223             if (w.indexOf(tag) > -1) {
19224                 return;
19225             }
19226             this.cblack.push(tag);
19227             
19228         }, this);
19229         
19230         Roo.each(b, function(tag) {
19231             if (w.indexOf(tag) > -1) {
19232                 return;
19233             }
19234             if (this.cblack.indexOf(tag) > -1) {
19235                 return;
19236             }
19237             this.cblack.push(tag);
19238             
19239         }, this);
19240     },
19241     
19242     setStylesheets : function(stylesheets)
19243     {
19244         if(typeof(stylesheets) == 'string'){
19245             Roo.get(this.iframe.contentDocument.head).createChild({
19246                 tag : 'link',
19247                 rel : 'stylesheet',
19248                 type : 'text/css',
19249                 href : stylesheets
19250             });
19251             
19252             return;
19253         }
19254         var _this = this;
19255      
19256         Roo.each(stylesheets, function(s) {
19257             if(!s.length){
19258                 return;
19259             }
19260             
19261             Roo.get(_this.iframe.contentDocument.head).createChild({
19262                 tag : 'link',
19263                 rel : 'stylesheet',
19264                 type : 'text/css',
19265                 href : s
19266             });
19267         });
19268
19269         
19270     },
19271     
19272     removeStylesheets : function()
19273     {
19274         var _this = this;
19275         
19276         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19277             s.remove();
19278         });
19279     }
19280     
19281     // hide stuff that is not compatible
19282     /**
19283      * @event blur
19284      * @hide
19285      */
19286     /**
19287      * @event change
19288      * @hide
19289      */
19290     /**
19291      * @event focus
19292      * @hide
19293      */
19294     /**
19295      * @event specialkey
19296      * @hide
19297      */
19298     /**
19299      * @cfg {String} fieldClass @hide
19300      */
19301     /**
19302      * @cfg {String} focusClass @hide
19303      */
19304     /**
19305      * @cfg {String} autoCreate @hide
19306      */
19307     /**
19308      * @cfg {String} inputType @hide
19309      */
19310     /**
19311      * @cfg {String} invalidClass @hide
19312      */
19313     /**
19314      * @cfg {String} invalidText @hide
19315      */
19316     /**
19317      * @cfg {String} msgFx @hide
19318      */
19319     /**
19320      * @cfg {String} validateOnBlur @hide
19321      */
19322 });
19323
19324 Roo.HtmlEditorCore.white = [
19325         'area', 'br', 'img', 'input', 'hr', 'wbr',
19326         
19327        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19328        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19329        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19330        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19331        'table',   'ul',         'xmp', 
19332        
19333        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19334       'thead',   'tr', 
19335      
19336       'dir', 'menu', 'ol', 'ul', 'dl',
19337        
19338       'embed',  'object'
19339 ];
19340
19341
19342 Roo.HtmlEditorCore.black = [
19343     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19344         'applet', // 
19345         'base',   'basefont', 'bgsound', 'blink',  'body', 
19346         'frame',  'frameset', 'head',    'html',   'ilayer', 
19347         'iframe', 'layer',  'link',     'meta',    'object',   
19348         'script', 'style' ,'title',  'xml' // clean later..
19349 ];
19350 Roo.HtmlEditorCore.clean = [
19351     'script', 'style', 'title', 'xml'
19352 ];
19353 Roo.HtmlEditorCore.remove = [
19354     'font'
19355 ];
19356 // attributes..
19357
19358 Roo.HtmlEditorCore.ablack = [
19359     'on'
19360 ];
19361     
19362 Roo.HtmlEditorCore.aclean = [ 
19363     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19364 ];
19365
19366 // protocols..
19367 Roo.HtmlEditorCore.pwhite= [
19368         'http',  'https',  'mailto'
19369 ];
19370
19371 // white listed style attributes.
19372 Roo.HtmlEditorCore.cwhite= [
19373       //  'text-align', /// default is to allow most things..
19374       
19375          
19376 //        'font-size'//??
19377 ];
19378
19379 // black listed style attributes.
19380 Roo.HtmlEditorCore.cblack= [
19381       //  'font-size' -- this can be set by the project 
19382 ];
19383
19384
19385 Roo.HtmlEditorCore.swapCodes   =[ 
19386     [    8211, "--" ], 
19387     [    8212, "--" ], 
19388     [    8216,  "'" ],  
19389     [    8217, "'" ],  
19390     [    8220, '"' ],  
19391     [    8221, '"' ],  
19392     [    8226, "*" ],  
19393     [    8230, "..." ]
19394 ]; 
19395
19396     /*
19397  * - LGPL
19398  *
19399  * HtmlEditor
19400  * 
19401  */
19402
19403 /**
19404  * @class Roo.bootstrap.HtmlEditor
19405  * @extends Roo.bootstrap.TextArea
19406  * Bootstrap HtmlEditor class
19407
19408  * @constructor
19409  * Create a new HtmlEditor
19410  * @param {Object} config The config object
19411  */
19412
19413 Roo.bootstrap.HtmlEditor = function(config){
19414     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19415     if (!this.toolbars) {
19416         this.toolbars = [];
19417     }
19418     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19419     this.addEvents({
19420             /**
19421              * @event initialize
19422              * Fires when the editor is fully initialized (including the iframe)
19423              * @param {HtmlEditor} this
19424              */
19425             initialize: true,
19426             /**
19427              * @event activate
19428              * Fires when the editor is first receives the focus. Any insertion must wait
19429              * until after this event.
19430              * @param {HtmlEditor} this
19431              */
19432             activate: true,
19433              /**
19434              * @event beforesync
19435              * Fires before the textarea is updated with content from the editor iframe. Return false
19436              * to cancel the sync.
19437              * @param {HtmlEditor} this
19438              * @param {String} html
19439              */
19440             beforesync: true,
19441              /**
19442              * @event beforepush
19443              * Fires before the iframe editor is updated with content from the textarea. Return false
19444              * to cancel the push.
19445              * @param {HtmlEditor} this
19446              * @param {String} html
19447              */
19448             beforepush: true,
19449              /**
19450              * @event sync
19451              * Fires when the textarea is updated with content from the editor iframe.
19452              * @param {HtmlEditor} this
19453              * @param {String} html
19454              */
19455             sync: true,
19456              /**
19457              * @event push
19458              * Fires when the iframe editor is updated with content from the textarea.
19459              * @param {HtmlEditor} this
19460              * @param {String} html
19461              */
19462             push: true,
19463              /**
19464              * @event editmodechange
19465              * Fires when the editor switches edit modes
19466              * @param {HtmlEditor} this
19467              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19468              */
19469             editmodechange: true,
19470             /**
19471              * @event editorevent
19472              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19473              * @param {HtmlEditor} this
19474              */
19475             editorevent: true,
19476             /**
19477              * @event firstfocus
19478              * Fires when on first focus - needed by toolbars..
19479              * @param {HtmlEditor} this
19480              */
19481             firstfocus: true,
19482             /**
19483              * @event autosave
19484              * Auto save the htmlEditor value as a file into Events
19485              * @param {HtmlEditor} this
19486              */
19487             autosave: true,
19488             /**
19489              * @event savedpreview
19490              * preview the saved version of htmlEditor
19491              * @param {HtmlEditor} this
19492              */
19493             savedpreview: true
19494         });
19495 };
19496
19497
19498 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19499     
19500     
19501       /**
19502      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19503      */
19504     toolbars : false,
19505    
19506      /**
19507      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19508      *                        Roo.resizable.
19509      */
19510     resizable : false,
19511      /**
19512      * @cfg {Number} height (in pixels)
19513      */   
19514     height: 300,
19515    /**
19516      * @cfg {Number} width (in pixels)
19517      */   
19518     width: false,
19519     
19520     /**
19521      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19522      * 
19523      */
19524     stylesheets: false,
19525     
19526     // id of frame..
19527     frameId: false,
19528     
19529     // private properties
19530     validationEvent : false,
19531     deferHeight: true,
19532     initialized : false,
19533     activated : false,
19534     
19535     onFocus : Roo.emptyFn,
19536     iframePad:3,
19537     hideMode:'offsets',
19538     
19539     
19540     tbContainer : false,
19541     
19542     toolbarContainer :function() {
19543         return this.wrap.select('.x-html-editor-tb',true).first();
19544     },
19545
19546     /**
19547      * Protected method that will not generally be called directly. It
19548      * is called when the editor creates its toolbar. Override this method if you need to
19549      * add custom toolbar buttons.
19550      * @param {HtmlEditor} editor
19551      */
19552     createToolbar : function(){
19553         
19554         Roo.log("create toolbars");
19555         
19556         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19557         this.toolbars[0].render(this.toolbarContainer());
19558         
19559         return;
19560         
19561 //        if (!editor.toolbars || !editor.toolbars.length) {
19562 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19563 //        }
19564 //        
19565 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19566 //            editor.toolbars[i] = Roo.factory(
19567 //                    typeof(editor.toolbars[i]) == 'string' ?
19568 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19569 //                Roo.bootstrap.HtmlEditor);
19570 //            editor.toolbars[i].init(editor);
19571 //        }
19572     },
19573
19574      
19575     // private
19576     onRender : function(ct, position)
19577     {
19578        // Roo.log("Call onRender: " + this.xtype);
19579         var _t = this;
19580         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19581       
19582         this.wrap = this.inputEl().wrap({
19583             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19584         });
19585         
19586         this.editorcore.onRender(ct, position);
19587          
19588         if (this.resizable) {
19589             this.resizeEl = new Roo.Resizable(this.wrap, {
19590                 pinned : true,
19591                 wrap: true,
19592                 dynamic : true,
19593                 minHeight : this.height,
19594                 height: this.height,
19595                 handles : this.resizable,
19596                 width: this.width,
19597                 listeners : {
19598                     resize : function(r, w, h) {
19599                         _t.onResize(w,h); // -something
19600                     }
19601                 }
19602             });
19603             
19604         }
19605         this.createToolbar(this);
19606        
19607         
19608         if(!this.width && this.resizable){
19609             this.setSize(this.wrap.getSize());
19610         }
19611         if (this.resizeEl) {
19612             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19613             // should trigger onReize..
19614         }
19615         
19616     },
19617
19618     // private
19619     onResize : function(w, h)
19620     {
19621         Roo.log('resize: ' +w + ',' + h );
19622         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19623         var ew = false;
19624         var eh = false;
19625         
19626         if(this.inputEl() ){
19627             if(typeof w == 'number'){
19628                 var aw = w - this.wrap.getFrameWidth('lr');
19629                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19630                 ew = aw;
19631             }
19632             if(typeof h == 'number'){
19633                  var tbh = -11;  // fixme it needs to tool bar size!
19634                 for (var i =0; i < this.toolbars.length;i++) {
19635                     // fixme - ask toolbars for heights?
19636                     tbh += this.toolbars[i].el.getHeight();
19637                     //if (this.toolbars[i].footer) {
19638                     //    tbh += this.toolbars[i].footer.el.getHeight();
19639                     //}
19640                 }
19641               
19642                 
19643                 
19644                 
19645                 
19646                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19647                 ah -= 5; // knock a few pixes off for look..
19648                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19649                 var eh = ah;
19650             }
19651         }
19652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19653         this.editorcore.onResize(ew,eh);
19654         
19655     },
19656
19657     /**
19658      * Toggles the editor between standard and source edit mode.
19659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19660      */
19661     toggleSourceEdit : function(sourceEditMode)
19662     {
19663         this.editorcore.toggleSourceEdit(sourceEditMode);
19664         
19665         if(this.editorcore.sourceEditMode){
19666             Roo.log('editor - showing textarea');
19667             
19668 //            Roo.log('in');
19669 //            Roo.log(this.syncValue());
19670             this.syncValue();
19671             this.inputEl().removeClass(['hide', 'x-hidden']);
19672             this.inputEl().dom.removeAttribute('tabIndex');
19673             this.inputEl().focus();
19674         }else{
19675             Roo.log('editor - hiding textarea');
19676 //            Roo.log('out')
19677 //            Roo.log(this.pushValue()); 
19678             this.pushValue();
19679             
19680             this.inputEl().addClass(['hide', 'x-hidden']);
19681             this.inputEl().dom.setAttribute('tabIndex', -1);
19682             //this.deferFocus();
19683         }
19684          
19685         if(this.resizable){
19686             this.setSize(this.wrap.getSize());
19687         }
19688         
19689         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19690     },
19691  
19692     // private (for BoxComponent)
19693     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19694
19695     // private (for BoxComponent)
19696     getResizeEl : function(){
19697         return this.wrap;
19698     },
19699
19700     // private (for BoxComponent)
19701     getPositionEl : function(){
19702         return this.wrap;
19703     },
19704
19705     // private
19706     initEvents : function(){
19707         this.originalValue = this.getValue();
19708     },
19709
19710 //    /**
19711 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19712 //     * @method
19713 //     */
19714 //    markInvalid : Roo.emptyFn,
19715 //    /**
19716 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19717 //     * @method
19718 //     */
19719 //    clearInvalid : Roo.emptyFn,
19720
19721     setValue : function(v){
19722         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19723         this.editorcore.pushValue();
19724     },
19725
19726      
19727     // private
19728     deferFocus : function(){
19729         this.focus.defer(10, this);
19730     },
19731
19732     // doc'ed in Field
19733     focus : function(){
19734         this.editorcore.focus();
19735         
19736     },
19737       
19738
19739     // private
19740     onDestroy : function(){
19741         
19742         
19743         
19744         if(this.rendered){
19745             
19746             for (var i =0; i < this.toolbars.length;i++) {
19747                 // fixme - ask toolbars for heights?
19748                 this.toolbars[i].onDestroy();
19749             }
19750             
19751             this.wrap.dom.innerHTML = '';
19752             this.wrap.remove();
19753         }
19754     },
19755
19756     // private
19757     onFirstFocus : function(){
19758         //Roo.log("onFirstFocus");
19759         this.editorcore.onFirstFocus();
19760          for (var i =0; i < this.toolbars.length;i++) {
19761             this.toolbars[i].onFirstFocus();
19762         }
19763         
19764     },
19765     
19766     // private
19767     syncValue : function()
19768     {   
19769         this.editorcore.syncValue();
19770     },
19771     
19772     pushValue : function()
19773     {   
19774         this.editorcore.pushValue();
19775     }
19776      
19777     
19778     // hide stuff that is not compatible
19779     /**
19780      * @event blur
19781      * @hide
19782      */
19783     /**
19784      * @event change
19785      * @hide
19786      */
19787     /**
19788      * @event focus
19789      * @hide
19790      */
19791     /**
19792      * @event specialkey
19793      * @hide
19794      */
19795     /**
19796      * @cfg {String} fieldClass @hide
19797      */
19798     /**
19799      * @cfg {String} focusClass @hide
19800      */
19801     /**
19802      * @cfg {String} autoCreate @hide
19803      */
19804     /**
19805      * @cfg {String} inputType @hide
19806      */
19807     /**
19808      * @cfg {String} invalidClass @hide
19809      */
19810     /**
19811      * @cfg {String} invalidText @hide
19812      */
19813     /**
19814      * @cfg {String} msgFx @hide
19815      */
19816     /**
19817      * @cfg {String} validateOnBlur @hide
19818      */
19819 });
19820  
19821     
19822    
19823    
19824    
19825       
19826 Roo.namespace('Roo.bootstrap.htmleditor');
19827 /**
19828  * @class Roo.bootstrap.HtmlEditorToolbar1
19829  * Basic Toolbar
19830  * 
19831  * Usage:
19832  *
19833  new Roo.bootstrap.HtmlEditor({
19834     ....
19835     toolbars : [
19836         new Roo.bootstrap.HtmlEditorToolbar1({
19837             disable : { fonts: 1 , format: 1, ..., ... , ...],
19838             btns : [ .... ]
19839         })
19840     }
19841      
19842  * 
19843  * @cfg {Object} disable List of elements to disable..
19844  * @cfg {Array} btns List of additional buttons.
19845  * 
19846  * 
19847  * NEEDS Extra CSS? 
19848  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19849  */
19850  
19851 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19852 {
19853     
19854     Roo.apply(this, config);
19855     
19856     // default disabled, based on 'good practice'..
19857     this.disable = this.disable || {};
19858     Roo.applyIf(this.disable, {
19859         fontSize : true,
19860         colors : true,
19861         specialElements : true
19862     });
19863     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19864     
19865     this.editor = config.editor;
19866     this.editorcore = config.editor.editorcore;
19867     
19868     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19869     
19870     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19871     // dont call parent... till later.
19872 }
19873 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19874      
19875     bar : true,
19876     
19877     editor : false,
19878     editorcore : false,
19879     
19880     
19881     formats : [
19882         "p" ,  
19883         "h1","h2","h3","h4","h5","h6", 
19884         "pre", "code", 
19885         "abbr", "acronym", "address", "cite", "samp", "var",
19886         'div','span'
19887     ],
19888     
19889     onRender : function(ct, position)
19890     {
19891        // Roo.log("Call onRender: " + this.xtype);
19892         
19893        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19894        Roo.log(this.el);
19895        this.el.dom.style.marginBottom = '0';
19896        var _this = this;
19897        var editorcore = this.editorcore;
19898        var editor= this.editor;
19899        
19900        var children = [];
19901        var btn = function(id,cmd , toggle, handler){
19902        
19903             var  event = toggle ? 'toggle' : 'click';
19904        
19905             var a = {
19906                 size : 'sm',
19907                 xtype: 'Button',
19908                 xns: Roo.bootstrap,
19909                 glyphicon : id,
19910                 cmd : id || cmd,
19911                 enableToggle:toggle !== false,
19912                 //html : 'submit'
19913                 pressed : toggle ? false : null,
19914                 listeners : {}
19915             }
19916             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19917                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19918             }
19919             children.push(a);
19920             return a;
19921        }
19922         
19923         var style = {
19924                 xtype: 'Button',
19925                 size : 'sm',
19926                 xns: Roo.bootstrap,
19927                 glyphicon : 'font',
19928                 //html : 'submit'
19929                 menu : {
19930                     xtype: 'Menu',
19931                     xns: Roo.bootstrap,
19932                     items:  []
19933                 }
19934         };
19935         Roo.each(this.formats, function(f) {
19936             style.menu.items.push({
19937                 xtype :'MenuItem',
19938                 xns: Roo.bootstrap,
19939                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19940                 tagname : f,
19941                 listeners : {
19942                     click : function()
19943                     {
19944                         editorcore.insertTag(this.tagname);
19945                         editor.focus();
19946                     }
19947                 }
19948                 
19949             });
19950         });
19951          children.push(style);   
19952             
19953             
19954         btn('bold',false,true);
19955         btn('italic',false,true);
19956         btn('align-left', 'justifyleft',true);
19957         btn('align-center', 'justifycenter',true);
19958         btn('align-right' , 'justifyright',true);
19959         btn('link', false, false, function(btn) {
19960             //Roo.log("create link?");
19961             var url = prompt(this.createLinkText, this.defaultLinkValue);
19962             if(url && url != 'http:/'+'/'){
19963                 this.editorcore.relayCmd('createlink', url);
19964             }
19965         }),
19966         btn('list','insertunorderedlist',true);
19967         btn('pencil', false,true, function(btn){
19968                 Roo.log(this);
19969                 
19970                 this.toggleSourceEdit(btn.pressed);
19971         });
19972         /*
19973         var cog = {
19974                 xtype: 'Button',
19975                 size : 'sm',
19976                 xns: Roo.bootstrap,
19977                 glyphicon : 'cog',
19978                 //html : 'submit'
19979                 menu : {
19980                     xtype: 'Menu',
19981                     xns: Roo.bootstrap,
19982                     items:  []
19983                 }
19984         };
19985         
19986         cog.menu.items.push({
19987             xtype :'MenuItem',
19988             xns: Roo.bootstrap,
19989             html : Clean styles,
19990             tagname : f,
19991             listeners : {
19992                 click : function()
19993                 {
19994                     editorcore.insertTag(this.tagname);
19995                     editor.focus();
19996                 }
19997             }
19998             
19999         });
20000        */
20001         
20002          
20003        this.xtype = 'NavSimplebar';
20004         
20005         for(var i=0;i< children.length;i++) {
20006             
20007             this.buttons.add(this.addxtypeChild(children[i]));
20008             
20009         }
20010         
20011         editor.on('editorevent', this.updateToolbar, this);
20012     },
20013     onBtnClick : function(id)
20014     {
20015        this.editorcore.relayCmd(id);
20016        this.editorcore.focus();
20017     },
20018     
20019     /**
20020      * Protected method that will not generally be called directly. It triggers
20021      * a toolbar update by reading the markup state of the current selection in the editor.
20022      */
20023     updateToolbar: function(){
20024
20025         if(!this.editorcore.activated){
20026             this.editor.onFirstFocus(); // is this neeed?
20027             return;
20028         }
20029
20030         var btns = this.buttons; 
20031         var doc = this.editorcore.doc;
20032         btns.get('bold').setActive(doc.queryCommandState('bold'));
20033         btns.get('italic').setActive(doc.queryCommandState('italic'));
20034         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20035         
20036         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20037         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20038         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20039         
20040         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20041         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20042          /*
20043         
20044         var ans = this.editorcore.getAllAncestors();
20045         if (this.formatCombo) {
20046             
20047             
20048             var store = this.formatCombo.store;
20049             this.formatCombo.setValue("");
20050             for (var i =0; i < ans.length;i++) {
20051                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20052                     // select it..
20053                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20054                     break;
20055                 }
20056             }
20057         }
20058         
20059         
20060         
20061         // hides menus... - so this cant be on a menu...
20062         Roo.bootstrap.MenuMgr.hideAll();
20063         */
20064         Roo.bootstrap.MenuMgr.hideAll();
20065         //this.editorsyncValue();
20066     },
20067     onFirstFocus: function() {
20068         this.buttons.each(function(item){
20069            item.enable();
20070         });
20071     },
20072     toggleSourceEdit : function(sourceEditMode){
20073         
20074           
20075         if(sourceEditMode){
20076             Roo.log("disabling buttons");
20077            this.buttons.each( function(item){
20078                 if(item.cmd != 'pencil'){
20079                     item.disable();
20080                 }
20081             });
20082           
20083         }else{
20084             Roo.log("enabling buttons");
20085             if(this.editorcore.initialized){
20086                 this.buttons.each( function(item){
20087                     item.enable();
20088                 });
20089             }
20090             
20091         }
20092         Roo.log("calling toggole on editor");
20093         // tell the editor that it's been pressed..
20094         this.editor.toggleSourceEdit(sourceEditMode);
20095        
20096     }
20097 });
20098
20099
20100
20101
20102
20103 /**
20104  * @class Roo.bootstrap.Table.AbstractSelectionModel
20105  * @extends Roo.util.Observable
20106  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20107  * implemented by descendant classes.  This class should not be directly instantiated.
20108  * @constructor
20109  */
20110 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20111     this.locked = false;
20112     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20113 };
20114
20115
20116 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20117     /** @ignore Called by the grid automatically. Do not call directly. */
20118     init : function(grid){
20119         this.grid = grid;
20120         this.initEvents();
20121     },
20122
20123     /**
20124      * Locks the selections.
20125      */
20126     lock : function(){
20127         this.locked = true;
20128     },
20129
20130     /**
20131      * Unlocks the selections.
20132      */
20133     unlock : function(){
20134         this.locked = false;
20135     },
20136
20137     /**
20138      * Returns true if the selections are locked.
20139      * @return {Boolean}
20140      */
20141     isLocked : function(){
20142         return this.locked;
20143     }
20144 });
20145 /**
20146  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20147  * @class Roo.bootstrap.Table.RowSelectionModel
20148  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20149  * It supports multiple selections and keyboard selection/navigation. 
20150  * @constructor
20151  * @param {Object} config
20152  */
20153
20154 Roo.bootstrap.Table.RowSelectionModel = function(config){
20155     Roo.apply(this, config);
20156     this.selections = new Roo.util.MixedCollection(false, function(o){
20157         return o.id;
20158     });
20159
20160     this.last = false;
20161     this.lastActive = false;
20162
20163     this.addEvents({
20164         /**
20165              * @event selectionchange
20166              * Fires when the selection changes
20167              * @param {SelectionModel} this
20168              */
20169             "selectionchange" : true,
20170         /**
20171              * @event afterselectionchange
20172              * Fires after the selection changes (eg. by key press or clicking)
20173              * @param {SelectionModel} this
20174              */
20175             "afterselectionchange" : true,
20176         /**
20177              * @event beforerowselect
20178              * Fires when a row is selected being selected, return false to cancel.
20179              * @param {SelectionModel} this
20180              * @param {Number} rowIndex The selected index
20181              * @param {Boolean} keepExisting False if other selections will be cleared
20182              */
20183             "beforerowselect" : true,
20184         /**
20185              * @event rowselect
20186              * Fires when a row is selected.
20187              * @param {SelectionModel} this
20188              * @param {Number} rowIndex The selected index
20189              * @param {Roo.data.Record} r The record
20190              */
20191             "rowselect" : true,
20192         /**
20193              * @event rowdeselect
20194              * Fires when a row is deselected.
20195              * @param {SelectionModel} this
20196              * @param {Number} rowIndex The selected index
20197              */
20198         "rowdeselect" : true
20199     });
20200     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20201     this.locked = false;
20202 };
20203
20204 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20205     /**
20206      * @cfg {Boolean} singleSelect
20207      * True to allow selection of only one row at a time (defaults to false)
20208      */
20209     singleSelect : false,
20210
20211     // private
20212     initEvents : function(){
20213
20214         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20215             this.grid.on("mousedown", this.handleMouseDown, this);
20216         }else{ // allow click to work like normal
20217             this.grid.on("rowclick", this.handleDragableRowClick, this);
20218         }
20219
20220         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20221             "up" : function(e){
20222                 if(!e.shiftKey){
20223                     this.selectPrevious(e.shiftKey);
20224                 }else if(this.last !== false && this.lastActive !== false){
20225                     var last = this.last;
20226                     this.selectRange(this.last,  this.lastActive-1);
20227                     this.grid.getView().focusRow(this.lastActive);
20228                     if(last !== false){
20229                         this.last = last;
20230                     }
20231                 }else{
20232                     this.selectFirstRow();
20233                 }
20234                 this.fireEvent("afterselectionchange", this);
20235             },
20236             "down" : function(e){
20237                 if(!e.shiftKey){
20238                     this.selectNext(e.shiftKey);
20239                 }else if(this.last !== false && this.lastActive !== false){
20240                     var last = this.last;
20241                     this.selectRange(this.last,  this.lastActive+1);
20242                     this.grid.getView().focusRow(this.lastActive);
20243                     if(last !== false){
20244                         this.last = last;
20245                     }
20246                 }else{
20247                     this.selectFirstRow();
20248                 }
20249                 this.fireEvent("afterselectionchange", this);
20250             },
20251             scope: this
20252         });
20253
20254         var view = this.grid.view;
20255         view.on("refresh", this.onRefresh, this);
20256         view.on("rowupdated", this.onRowUpdated, this);
20257         view.on("rowremoved", this.onRemove, this);
20258     },
20259
20260     // private
20261     onRefresh : function(){
20262         var ds = this.grid.dataSource, i, v = this.grid.view;
20263         var s = this.selections;
20264         s.each(function(r){
20265             if((i = ds.indexOfId(r.id)) != -1){
20266                 v.onRowSelect(i);
20267             }else{
20268                 s.remove(r);
20269             }
20270         });
20271     },
20272
20273     // private
20274     onRemove : function(v, index, r){
20275         this.selections.remove(r);
20276     },
20277
20278     // private
20279     onRowUpdated : function(v, index, r){
20280         if(this.isSelected(r)){
20281             v.onRowSelect(index);
20282         }
20283     },
20284
20285     /**
20286      * Select records.
20287      * @param {Array} records The records to select
20288      * @param {Boolean} keepExisting (optional) True to keep existing selections
20289      */
20290     selectRecords : function(records, keepExisting){
20291         if(!keepExisting){
20292             this.clearSelections();
20293         }
20294         var ds = this.grid.dataSource;
20295         for(var i = 0, len = records.length; i < len; i++){
20296             this.selectRow(ds.indexOf(records[i]), true);
20297         }
20298     },
20299
20300     /**
20301      * Gets the number of selected rows.
20302      * @return {Number}
20303      */
20304     getCount : function(){
20305         return this.selections.length;
20306     },
20307
20308     /**
20309      * Selects the first row in the grid.
20310      */
20311     selectFirstRow : function(){
20312         this.selectRow(0);
20313     },
20314
20315     /**
20316      * Select the last row.
20317      * @param {Boolean} keepExisting (optional) True to keep existing selections
20318      */
20319     selectLastRow : function(keepExisting){
20320         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20321     },
20322
20323     /**
20324      * Selects the row immediately following the last selected row.
20325      * @param {Boolean} keepExisting (optional) True to keep existing selections
20326      */
20327     selectNext : function(keepExisting){
20328         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20329             this.selectRow(this.last+1, keepExisting);
20330             this.grid.getView().focusRow(this.last);
20331         }
20332     },
20333
20334     /**
20335      * Selects the row that precedes the last selected row.
20336      * @param {Boolean} keepExisting (optional) True to keep existing selections
20337      */
20338     selectPrevious : function(keepExisting){
20339         if(this.last){
20340             this.selectRow(this.last-1, keepExisting);
20341             this.grid.getView().focusRow(this.last);
20342         }
20343     },
20344
20345     /**
20346      * Returns the selected records
20347      * @return {Array} Array of selected records
20348      */
20349     getSelections : function(){
20350         return [].concat(this.selections.items);
20351     },
20352
20353     /**
20354      * Returns the first selected record.
20355      * @return {Record}
20356      */
20357     getSelected : function(){
20358         return this.selections.itemAt(0);
20359     },
20360
20361
20362     /**
20363      * Clears all selections.
20364      */
20365     clearSelections : function(fast){
20366         if(this.locked) return;
20367         if(fast !== true){
20368             var ds = this.grid.dataSource;
20369             var s = this.selections;
20370             s.each(function(r){
20371                 this.deselectRow(ds.indexOfId(r.id));
20372             }, this);
20373             s.clear();
20374         }else{
20375             this.selections.clear();
20376         }
20377         this.last = false;
20378     },
20379
20380
20381     /**
20382      * Selects all rows.
20383      */
20384     selectAll : function(){
20385         if(this.locked) return;
20386         this.selections.clear();
20387         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20388             this.selectRow(i, true);
20389         }
20390     },
20391
20392     /**
20393      * Returns True if there is a selection.
20394      * @return {Boolean}
20395      */
20396     hasSelection : function(){
20397         return this.selections.length > 0;
20398     },
20399
20400     /**
20401      * Returns True if the specified row is selected.
20402      * @param {Number/Record} record The record or index of the record to check
20403      * @return {Boolean}
20404      */
20405     isSelected : function(index){
20406         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20407         return (r && this.selections.key(r.id) ? true : false);
20408     },
20409
20410     /**
20411      * Returns True if the specified record id is selected.
20412      * @param {String} id The id of record to check
20413      * @return {Boolean}
20414      */
20415     isIdSelected : function(id){
20416         return (this.selections.key(id) ? true : false);
20417     },
20418
20419     // private
20420     handleMouseDown : function(e, t){
20421         var view = this.grid.getView(), rowIndex;
20422         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20423             return;
20424         };
20425         if(e.shiftKey && this.last !== false){
20426             var last = this.last;
20427             this.selectRange(last, rowIndex, e.ctrlKey);
20428             this.last = last; // reset the last
20429             view.focusRow(rowIndex);
20430         }else{
20431             var isSelected = this.isSelected(rowIndex);
20432             if(e.button !== 0 && isSelected){
20433                 view.focusRow(rowIndex);
20434             }else if(e.ctrlKey && isSelected){
20435                 this.deselectRow(rowIndex);
20436             }else if(!isSelected){
20437                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20438                 view.focusRow(rowIndex);
20439             }
20440         }
20441         this.fireEvent("afterselectionchange", this);
20442     },
20443     // private
20444     handleDragableRowClick :  function(grid, rowIndex, e) 
20445     {
20446         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20447             this.selectRow(rowIndex, false);
20448             grid.view.focusRow(rowIndex);
20449              this.fireEvent("afterselectionchange", this);
20450         }
20451     },
20452     
20453     /**
20454      * Selects multiple rows.
20455      * @param {Array} rows Array of the indexes of the row to select
20456      * @param {Boolean} keepExisting (optional) True to keep existing selections
20457      */
20458     selectRows : function(rows, keepExisting){
20459         if(!keepExisting){
20460             this.clearSelections();
20461         }
20462         for(var i = 0, len = rows.length; i < len; i++){
20463             this.selectRow(rows[i], true);
20464         }
20465     },
20466
20467     /**
20468      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20469      * @param {Number} startRow The index of the first row in the range
20470      * @param {Number} endRow The index of the last row in the range
20471      * @param {Boolean} keepExisting (optional) True to retain existing selections
20472      */
20473     selectRange : function(startRow, endRow, keepExisting){
20474         if(this.locked) return;
20475         if(!keepExisting){
20476             this.clearSelections();
20477         }
20478         if(startRow <= endRow){
20479             for(var i = startRow; i <= endRow; i++){
20480                 this.selectRow(i, true);
20481             }
20482         }else{
20483             for(var i = startRow; i >= endRow; i--){
20484                 this.selectRow(i, true);
20485             }
20486         }
20487     },
20488
20489     /**
20490      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20491      * @param {Number} startRow The index of the first row in the range
20492      * @param {Number} endRow The index of the last row in the range
20493      */
20494     deselectRange : function(startRow, endRow, preventViewNotify){
20495         if(this.locked) return;
20496         for(var i = startRow; i <= endRow; i++){
20497             this.deselectRow(i, preventViewNotify);
20498         }
20499     },
20500
20501     /**
20502      * Selects a row.
20503      * @param {Number} row The index of the row to select
20504      * @param {Boolean} keepExisting (optional) True to keep existing selections
20505      */
20506     selectRow : function(index, keepExisting, preventViewNotify){
20507         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20508         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20509             if(!keepExisting || this.singleSelect){
20510                 this.clearSelections();
20511             }
20512             var r = this.grid.dataSource.getAt(index);
20513             this.selections.add(r);
20514             this.last = this.lastActive = index;
20515             if(!preventViewNotify){
20516                 this.grid.getView().onRowSelect(index);
20517             }
20518             this.fireEvent("rowselect", this, index, r);
20519             this.fireEvent("selectionchange", this);
20520         }
20521     },
20522
20523     /**
20524      * Deselects a row.
20525      * @param {Number} row The index of the row to deselect
20526      */
20527     deselectRow : function(index, preventViewNotify){
20528         if(this.locked) return;
20529         if(this.last == index){
20530             this.last = false;
20531         }
20532         if(this.lastActive == index){
20533             this.lastActive = false;
20534         }
20535         var r = this.grid.dataSource.getAt(index);
20536         this.selections.remove(r);
20537         if(!preventViewNotify){
20538             this.grid.getView().onRowDeselect(index);
20539         }
20540         this.fireEvent("rowdeselect", this, index);
20541         this.fireEvent("selectionchange", this);
20542     },
20543
20544     // private
20545     restoreLast : function(){
20546         if(this._last){
20547             this.last = this._last;
20548         }
20549     },
20550
20551     // private
20552     acceptsNav : function(row, col, cm){
20553         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20554     },
20555
20556     // private
20557     onEditorKey : function(field, e){
20558         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20559         if(k == e.TAB){
20560             e.stopEvent();
20561             ed.completeEdit();
20562             if(e.shiftKey){
20563                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20564             }else{
20565                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20566             }
20567         }else if(k == e.ENTER && !e.ctrlKey){
20568             e.stopEvent();
20569             ed.completeEdit();
20570             if(e.shiftKey){
20571                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20572             }else{
20573                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20574             }
20575         }else if(k == e.ESC){
20576             ed.cancelEdit();
20577         }
20578         if(newCell){
20579             g.startEditing(newCell[0], newCell[1]);
20580         }
20581     }
20582 });/*
20583  * Based on:
20584  * Ext JS Library 1.1.1
20585  * Copyright(c) 2006-2007, Ext JS, LLC.
20586  *
20587  * Originally Released Under LGPL - original licence link has changed is not relivant.
20588  *
20589  * Fork - LGPL
20590  * <script type="text/javascript">
20591  */
20592  
20593 /**
20594  * @class Roo.bootstrap.PagingToolbar
20595  * @extends Roo.Row
20596  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20597  * @constructor
20598  * Create a new PagingToolbar
20599  * @param {Object} config The config object
20600  */
20601 Roo.bootstrap.PagingToolbar = function(config)
20602 {
20603     // old args format still supported... - xtype is prefered..
20604         // created from xtype...
20605     var ds = config.dataSource;
20606     this.toolbarItems = [];
20607     if (config.items) {
20608         this.toolbarItems = config.items;
20609 //        config.items = [];
20610     }
20611     
20612     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20613     this.ds = ds;
20614     this.cursor = 0;
20615     if (ds) { 
20616         this.bind(ds);
20617     }
20618     
20619     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20620     
20621 };
20622
20623 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20624     /**
20625      * @cfg {Roo.data.Store} dataSource
20626      * The underlying data store providing the paged data
20627      */
20628     /**
20629      * @cfg {String/HTMLElement/Element} container
20630      * container The id or element that will contain the toolbar
20631      */
20632     /**
20633      * @cfg {Boolean} displayInfo
20634      * True to display the displayMsg (defaults to false)
20635      */
20636     /**
20637      * @cfg {Number} pageSize
20638      * The number of records to display per page (defaults to 20)
20639      */
20640     pageSize: 20,
20641     /**
20642      * @cfg {String} displayMsg
20643      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20644      */
20645     displayMsg : 'Displaying {0} - {1} of {2}',
20646     /**
20647      * @cfg {String} emptyMsg
20648      * The message to display when no records are found (defaults to "No data to display")
20649      */
20650     emptyMsg : 'No data to display',
20651     /**
20652      * Customizable piece of the default paging text (defaults to "Page")
20653      * @type String
20654      */
20655     beforePageText : "Page",
20656     /**
20657      * Customizable piece of the default paging text (defaults to "of %0")
20658      * @type String
20659      */
20660     afterPageText : "of {0}",
20661     /**
20662      * Customizable piece of the default paging text (defaults to "First Page")
20663      * @type String
20664      */
20665     firstText : "First Page",
20666     /**
20667      * Customizable piece of the default paging text (defaults to "Previous Page")
20668      * @type String
20669      */
20670     prevText : "Previous Page",
20671     /**
20672      * Customizable piece of the default paging text (defaults to "Next Page")
20673      * @type String
20674      */
20675     nextText : "Next Page",
20676     /**
20677      * Customizable piece of the default paging text (defaults to "Last Page")
20678      * @type String
20679      */
20680     lastText : "Last Page",
20681     /**
20682      * Customizable piece of the default paging text (defaults to "Refresh")
20683      * @type String
20684      */
20685     refreshText : "Refresh",
20686
20687     buttons : false,
20688     // private
20689     onRender : function(ct, position) 
20690     {
20691         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20692         this.navgroup.parentId = this.id;
20693         this.navgroup.onRender(this.el, null);
20694         // add the buttons to the navgroup
20695         
20696         if(this.displayInfo){
20697             Roo.log(this.el.select('ul.navbar-nav',true).first());
20698             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20699             this.displayEl = this.el.select('.x-paging-info', true).first();
20700 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20701 //            this.displayEl = navel.el.select('span',true).first();
20702         }
20703         
20704         var _this = this;
20705         
20706         if(this.buttons){
20707             Roo.each(_this.buttons, function(e){
20708                Roo.factory(e).onRender(_this.el, null);
20709             });
20710         }
20711             
20712         Roo.each(_this.toolbarItems, function(e) {
20713             _this.navgroup.addItem(e);
20714         });
20715         
20716         
20717         this.first = this.navgroup.addItem({
20718             tooltip: this.firstText,
20719             cls: "prev",
20720             icon : 'fa fa-backward',
20721             disabled: true,
20722             preventDefault: true,
20723             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20724         });
20725         
20726         this.prev =  this.navgroup.addItem({
20727             tooltip: this.prevText,
20728             cls: "prev",
20729             icon : 'fa fa-step-backward',
20730             disabled: true,
20731             preventDefault: true,
20732             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20733         });
20734     //this.addSeparator();
20735         
20736         
20737         var field = this.navgroup.addItem( {
20738             tagtype : 'span',
20739             cls : 'x-paging-position',
20740             
20741             html : this.beforePageText  +
20742                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20743                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20744          } ); //?? escaped?
20745         
20746         this.field = field.el.select('input', true).first();
20747         this.field.on("keydown", this.onPagingKeydown, this);
20748         this.field.on("focus", function(){this.dom.select();});
20749     
20750     
20751         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20752         //this.field.setHeight(18);
20753         //this.addSeparator();
20754         this.next = this.navgroup.addItem({
20755             tooltip: this.nextText,
20756             cls: "next",
20757             html : ' <i class="fa fa-step-forward">',
20758             disabled: true,
20759             preventDefault: true,
20760             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20761         });
20762         this.last = this.navgroup.addItem({
20763             tooltip: this.lastText,
20764             icon : 'fa fa-forward',
20765             cls: "next",
20766             disabled: true,
20767             preventDefault: true,
20768             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20769         });
20770     //this.addSeparator();
20771         this.loading = this.navgroup.addItem({
20772             tooltip: this.refreshText,
20773             icon: 'fa fa-refresh',
20774             preventDefault: true,
20775             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20776         });
20777
20778     },
20779
20780     // private
20781     updateInfo : function(){
20782         if(this.displayEl){
20783             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20784             var msg = count == 0 ?
20785                 this.emptyMsg :
20786                 String.format(
20787                     this.displayMsg,
20788                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20789                 );
20790             this.displayEl.update(msg);
20791         }
20792     },
20793
20794     // private
20795     onLoad : function(ds, r, o){
20796        this.cursor = o.params ? o.params.start : 0;
20797        var d = this.getPageData(),
20798             ap = d.activePage,
20799             ps = d.pages;
20800         
20801        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20802        this.field.dom.value = ap;
20803        this.first.setDisabled(ap == 1);
20804        this.prev.setDisabled(ap == 1);
20805        this.next.setDisabled(ap == ps);
20806        this.last.setDisabled(ap == ps);
20807        this.loading.enable();
20808        this.updateInfo();
20809     },
20810
20811     // private
20812     getPageData : function(){
20813         var total = this.ds.getTotalCount();
20814         return {
20815             total : total,
20816             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20817             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20818         };
20819     },
20820
20821     // private
20822     onLoadError : function(){
20823         this.loading.enable();
20824     },
20825
20826     // private
20827     onPagingKeydown : function(e){
20828         var k = e.getKey();
20829         var d = this.getPageData();
20830         if(k == e.RETURN){
20831             var v = this.field.dom.value, pageNum;
20832             if(!v || isNaN(pageNum = parseInt(v, 10))){
20833                 this.field.dom.value = d.activePage;
20834                 return;
20835             }
20836             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20837             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20838             e.stopEvent();
20839         }
20840         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))
20841         {
20842           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20843           this.field.dom.value = pageNum;
20844           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20845           e.stopEvent();
20846         }
20847         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20848         {
20849           var v = this.field.dom.value, pageNum; 
20850           var increment = (e.shiftKey) ? 10 : 1;
20851           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20852             increment *= -1;
20853           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20854             this.field.dom.value = d.activePage;
20855             return;
20856           }
20857           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20858           {
20859             this.field.dom.value = parseInt(v, 10) + increment;
20860             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20861             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20862           }
20863           e.stopEvent();
20864         }
20865     },
20866
20867     // private
20868     beforeLoad : function(){
20869         if(this.loading){
20870             this.loading.disable();
20871         }
20872     },
20873
20874     // private
20875     onClick : function(which){
20876         
20877         var ds = this.ds;
20878         if (!ds) {
20879             return;
20880         }
20881         
20882         switch(which){
20883             case "first":
20884                 ds.load({params:{start: 0, limit: this.pageSize}});
20885             break;
20886             case "prev":
20887                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20888             break;
20889             case "next":
20890                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20891             break;
20892             case "last":
20893                 var total = ds.getTotalCount();
20894                 var extra = total % this.pageSize;
20895                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20896                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20897             break;
20898             case "refresh":
20899                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20900             break;
20901         }
20902     },
20903
20904     /**
20905      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20906      * @param {Roo.data.Store} store The data store to unbind
20907      */
20908     unbind : function(ds){
20909         ds.un("beforeload", this.beforeLoad, this);
20910         ds.un("load", this.onLoad, this);
20911         ds.un("loadexception", this.onLoadError, this);
20912         ds.un("remove", this.updateInfo, this);
20913         ds.un("add", this.updateInfo, this);
20914         this.ds = undefined;
20915     },
20916
20917     /**
20918      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20919      * @param {Roo.data.Store} store The data store to bind
20920      */
20921     bind : function(ds){
20922         ds.on("beforeload", this.beforeLoad, this);
20923         ds.on("load", this.onLoad, this);
20924         ds.on("loadexception", this.onLoadError, this);
20925         ds.on("remove", this.updateInfo, this);
20926         ds.on("add", this.updateInfo, this);
20927         this.ds = ds;
20928     }
20929 });/*
20930  * - LGPL
20931  *
20932  * element
20933  * 
20934  */
20935
20936 /**
20937  * @class Roo.bootstrap.MessageBar
20938  * @extends Roo.bootstrap.Component
20939  * Bootstrap MessageBar class
20940  * @cfg {String} html contents of the MessageBar
20941  * @cfg {String} weight (info | success | warning | danger) default info
20942  * @cfg {String} beforeClass insert the bar before the given class
20943  * @cfg {Boolean} closable (true | false) default false
20944  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20945  * 
20946  * @constructor
20947  * Create a new Element
20948  * @param {Object} config The config object
20949  */
20950
20951 Roo.bootstrap.MessageBar = function(config){
20952     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20953 };
20954
20955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20956     
20957     html: '',
20958     weight: 'info',
20959     closable: false,
20960     fixed: false,
20961     beforeClass: 'bootstrap-sticky-wrap',
20962     
20963     getAutoCreate : function(){
20964         
20965         var cfg = {
20966             tag: 'div',
20967             cls: 'alert alert-dismissable alert-' + this.weight,
20968             cn: [
20969                 {
20970                     tag: 'span',
20971                     cls: 'message',
20972                     html: this.html || ''
20973                 }
20974             ]
20975         }
20976         
20977         if(this.fixed){
20978             cfg.cls += ' alert-messages-fixed';
20979         }
20980         
20981         if(this.closable){
20982             cfg.cn.push({
20983                 tag: 'button',
20984                 cls: 'close',
20985                 html: 'x'
20986             });
20987         }
20988         
20989         return cfg;
20990     },
20991     
20992     onRender : function(ct, position)
20993     {
20994         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20995         
20996         if(!this.el){
20997             var cfg = Roo.apply({},  this.getAutoCreate());
20998             cfg.id = Roo.id();
20999             
21000             if (this.cls) {
21001                 cfg.cls += ' ' + this.cls;
21002             }
21003             if (this.style) {
21004                 cfg.style = this.style;
21005             }
21006             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
21007             
21008             this.el.setVisibilityMode(Roo.Element.DISPLAY);
21009         }
21010         
21011         this.el.select('>button.close').on('click', this.hide, this);
21012         
21013     },
21014     
21015     show : function()
21016     {
21017         if (!this.rendered) {
21018             this.render();
21019         }
21020         
21021         this.el.show();
21022         
21023         this.fireEvent('show', this);
21024         
21025     },
21026     
21027     hide : function()
21028     {
21029         if (!this.rendered) {
21030             this.render();
21031         }
21032         
21033         this.el.hide();
21034         
21035         this.fireEvent('hide', this);
21036     },
21037     
21038     update : function()
21039     {
21040 //        var e = this.el.dom.firstChild;
21041 //        
21042 //        if(this.closable){
21043 //            e = e.nextSibling;
21044 //        }
21045 //        
21046 //        e.data = this.html || '';
21047
21048         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21049     }
21050    
21051 });
21052
21053  
21054
21055      /*
21056  * - LGPL
21057  *
21058  * Graph
21059  * 
21060  */
21061
21062
21063 /**
21064  * @class Roo.bootstrap.Graph
21065  * @extends Roo.bootstrap.Component
21066  * Bootstrap Graph class
21067 > Prameters
21068  -sm {number} sm 4
21069  -md {number} md 5
21070  @cfg {String} graphtype  bar | vbar | pie
21071  @cfg {number} g_x coodinator | centre x (pie)
21072  @cfg {number} g_y coodinator | centre y (pie)
21073  @cfg {number} g_r radius (pie)
21074  @cfg {number} g_height height of the chart (respected by all elements in the set)
21075  @cfg {number} g_width width of the chart (respected by all elements in the set)
21076  @cfg {Object} title The title of the chart
21077     
21078  -{Array}  values
21079  -opts (object) options for the chart 
21080      o {
21081      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21082      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21083      o vgutter (number)
21084      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.
21085      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21086      o to
21087      o stretch (boolean)
21088      o }
21089  -opts (object) options for the pie
21090      o{
21091      o cut
21092      o startAngle (number)
21093      o endAngle (number)
21094      } 
21095  *
21096  * @constructor
21097  * Create a new Input
21098  * @param {Object} config The config object
21099  */
21100
21101 Roo.bootstrap.Graph = function(config){
21102     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21103     
21104     this.addEvents({
21105         // img events
21106         /**
21107          * @event click
21108          * The img click event for the img.
21109          * @param {Roo.EventObject} e
21110          */
21111         "click" : true
21112     });
21113 };
21114
21115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21116     
21117     sm: 4,
21118     md: 5,
21119     graphtype: 'bar',
21120     g_height: 250,
21121     g_width: 400,
21122     g_x: 50,
21123     g_y: 50,
21124     g_r: 30,
21125     opts:{
21126         //g_colors: this.colors,
21127         g_type: 'soft',
21128         g_gutter: '20%'
21129
21130     },
21131     title : false,
21132
21133     getAutoCreate : function(){
21134         
21135         var cfg = {
21136             tag: 'div',
21137             html : null
21138         }
21139         
21140         
21141         return  cfg;
21142     },
21143
21144     onRender : function(ct,position){
21145         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21146         this.raphael = Raphael(this.el.dom);
21147         
21148                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21149                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21150                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21151                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21152                 /*
21153                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21154                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21155                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21156                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21157                 
21158                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21159                 r.barchart(330, 10, 300, 220, data1);
21160                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21161                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21162                 */
21163                 
21164                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21165                 // r.barchart(30, 30, 560, 250,  xdata, {
21166                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21167                 //     axis : "0 0 1 1",
21168                 //     axisxlabels :  xdata
21169                 //     //yvalues : cols,
21170                    
21171                 // });
21172 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21173 //        
21174 //        this.load(null,xdata,{
21175 //                axis : "0 0 1 1",
21176 //                axisxlabels :  xdata
21177 //                });
21178
21179     },
21180
21181     load : function(graphtype,xdata,opts){
21182         this.raphael.clear();
21183         if(!graphtype) {
21184             graphtype = this.graphtype;
21185         }
21186         if(!opts){
21187             opts = this.opts;
21188         }
21189         var r = this.raphael,
21190             fin = function () {
21191                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21192             },
21193             fout = function () {
21194                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21195             },
21196             pfin = function() {
21197                 this.sector.stop();
21198                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21199
21200                 if (this.label) {
21201                     this.label[0].stop();
21202                     this.label[0].attr({ r: 7.5 });
21203                     this.label[1].attr({ "font-weight": 800 });
21204                 }
21205             },
21206             pfout = function() {
21207                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21208
21209                 if (this.label) {
21210                     this.label[0].animate({ r: 5 }, 500, "bounce");
21211                     this.label[1].attr({ "font-weight": 400 });
21212                 }
21213             };
21214
21215         switch(graphtype){
21216             case 'bar':
21217                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21218                 break;
21219             case 'hbar':
21220                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21221                 break;
21222             case 'pie':
21223 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21224 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21225 //            
21226                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21227                 
21228                 break;
21229
21230         }
21231         
21232         if(this.title){
21233             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21234         }
21235         
21236     },
21237     
21238     setTitle: function(o)
21239     {
21240         this.title = o;
21241     },
21242     
21243     initEvents: function() {
21244         
21245         if(!this.href){
21246             this.el.on('click', this.onClick, this);
21247         }
21248     },
21249     
21250     onClick : function(e)
21251     {
21252         Roo.log('img onclick');
21253         this.fireEvent('click', this, e);
21254     }
21255    
21256 });
21257
21258  
21259 /*
21260  * - LGPL
21261  *
21262  * numberBox
21263  * 
21264  */
21265 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21266
21267 /**
21268  * @class Roo.bootstrap.dash.NumberBox
21269  * @extends Roo.bootstrap.Component
21270  * Bootstrap NumberBox class
21271  * @cfg {String} headline Box headline
21272  * @cfg {String} content Box content
21273  * @cfg {String} icon Box icon
21274  * @cfg {String} footer Footer text
21275  * @cfg {String} fhref Footer href
21276  * 
21277  * @constructor
21278  * Create a new NumberBox
21279  * @param {Object} config The config object
21280  */
21281
21282
21283 Roo.bootstrap.dash.NumberBox = function(config){
21284     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21285     
21286 };
21287
21288 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21289     
21290     headline : '',
21291     content : '',
21292     icon : '',
21293     footer : '',
21294     fhref : '',
21295     ficon : '',
21296     
21297     getAutoCreate : function(){
21298         
21299         var cfg = {
21300             tag : 'div',
21301             cls : 'small-box ',
21302             cn : [
21303                 {
21304                     tag : 'div',
21305                     cls : 'inner',
21306                     cn :[
21307                         {
21308                             tag : 'h3',
21309                             cls : 'roo-headline',
21310                             html : this.headline
21311                         },
21312                         {
21313                             tag : 'p',
21314                             cls : 'roo-content',
21315                             html : this.content
21316                         }
21317                     ]
21318                 }
21319             ]
21320         }
21321         
21322         if(this.icon){
21323             cfg.cn.push({
21324                 tag : 'div',
21325                 cls : 'icon',
21326                 cn :[
21327                     {
21328                         tag : 'i',
21329                         cls : 'ion ' + this.icon
21330                     }
21331                 ]
21332             });
21333         }
21334         
21335         if(this.footer){
21336             var footer = {
21337                 tag : 'a',
21338                 cls : 'small-box-footer',
21339                 href : this.fhref || '#',
21340                 html : this.footer
21341             };
21342             
21343             cfg.cn.push(footer);
21344             
21345         }
21346         
21347         return  cfg;
21348     },
21349
21350     onRender : function(ct,position){
21351         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21352
21353
21354        
21355                 
21356     },
21357
21358     setHeadline: function (value)
21359     {
21360         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21361     },
21362     
21363     setFooter: function (value, href)
21364     {
21365         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21366         
21367         if(href){
21368             this.el.select('a.small-box-footer',true).first().attr('href', href);
21369         }
21370         
21371     },
21372
21373     setContent: function (value)
21374     {
21375         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21376     },
21377
21378     initEvents: function() 
21379     {   
21380         
21381     }
21382     
21383 });
21384
21385  
21386 /*
21387  * - LGPL
21388  *
21389  * TabBox
21390  * 
21391  */
21392 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21393
21394 /**
21395  * @class Roo.bootstrap.dash.TabBox
21396  * @extends Roo.bootstrap.Component
21397  * Bootstrap TabBox class
21398  * @cfg {String} title Title of the TabBox
21399  * @cfg {String} icon Icon of the TabBox
21400  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21401  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21402  * 
21403  * @constructor
21404  * Create a new TabBox
21405  * @param {Object} config The config object
21406  */
21407
21408
21409 Roo.bootstrap.dash.TabBox = function(config){
21410     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21411     this.addEvents({
21412         // raw events
21413         /**
21414          * @event addpane
21415          * When a pane is added
21416          * @param {Roo.bootstrap.dash.TabPane} pane
21417          */
21418         "addpane" : true,
21419         /**
21420          * @event activatepane
21421          * When a pane is activated
21422          * @param {Roo.bootstrap.dash.TabPane} pane
21423          */
21424         "activatepane" : true
21425         
21426          
21427     });
21428     
21429     this.panes = [];
21430 };
21431
21432 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21433
21434     title : '',
21435     icon : false,
21436     showtabs : true,
21437     tabScrollable : false,
21438     
21439     getChildContainer : function()
21440     {
21441         return this.el.select('.tab-content', true).first();
21442     },
21443     
21444     getAutoCreate : function(){
21445         
21446         var header = {
21447             tag: 'li',
21448             cls: 'pull-left header',
21449             html: this.title,
21450             cn : []
21451         };
21452         
21453         if(this.icon){
21454             header.cn.push({
21455                 tag: 'i',
21456                 cls: 'fa ' + this.icon
21457             });
21458         }
21459         
21460         var h = {
21461             tag: 'ul',
21462             cls: 'nav nav-tabs pull-right',
21463             cn: [
21464                 header
21465             ]
21466         };
21467         
21468         if(this.tabScrollable){
21469             h = {
21470                 tag: 'div',
21471                 cls: 'tab-header',
21472                 cn: [
21473                     {
21474                         tag: 'ul',
21475                         cls: 'nav nav-tabs pull-right',
21476                         cn: [
21477                             header
21478                         ]
21479                     }
21480                 ]
21481             }
21482         }
21483         
21484         var cfg = {
21485             tag: 'div',
21486             cls: 'nav-tabs-custom',
21487             cn: [
21488                 h,
21489                 {
21490                     tag: 'div',
21491                     cls: 'tab-content no-padding',
21492                     cn: []
21493                 }
21494             ]
21495         }
21496
21497         return  cfg;
21498     },
21499     initEvents : function()
21500     {
21501         //Roo.log('add add pane handler');
21502         this.on('addpane', this.onAddPane, this);
21503     },
21504      /**
21505      * Updates the box title
21506      * @param {String} html to set the title to.
21507      */
21508     setTitle : function(value)
21509     {
21510         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21511     },
21512     onAddPane : function(pane)
21513     {
21514         this.panes.push(pane);
21515         //Roo.log('addpane');
21516         //Roo.log(pane);
21517         // tabs are rendere left to right..
21518         if(!this.showtabs){
21519             return;
21520         }
21521         
21522         var ctr = this.el.select('.nav-tabs', true).first();
21523          
21524          
21525         var existing = ctr.select('.nav-tab',true);
21526         var qty = existing.getCount();;
21527         
21528         
21529         var tab = ctr.createChild({
21530             tag : 'li',
21531             cls : 'nav-tab' + (qty ? '' : ' active'),
21532             cn : [
21533                 {
21534                     tag : 'a',
21535                     href:'#',
21536                     html : pane.title
21537                 }
21538             ]
21539         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21540         pane.tab = tab;
21541         
21542         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21543         if (!qty) {
21544             pane.el.addClass('active');
21545         }
21546         
21547                 
21548     },
21549     onTabClick : function(ev,un,ob,pane)
21550     {
21551         //Roo.log('tab - prev default');
21552         ev.preventDefault();
21553         
21554         
21555         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21556         pane.tab.addClass('active');
21557         //Roo.log(pane.title);
21558         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21559         // technically we should have a deactivate event.. but maybe add later.
21560         // and it should not de-activate the selected tab...
21561         this.fireEvent('activatepane', pane);
21562         pane.el.addClass('active');
21563         pane.fireEvent('activate');
21564         
21565         
21566     },
21567     
21568     getActivePane : function()
21569     {
21570         var r = false;
21571         Roo.each(this.panes, function(p) {
21572             if(p.el.hasClass('active')){
21573                 r = p;
21574                 return false;
21575             }
21576             
21577             return;
21578         });
21579         
21580         return r;
21581     }
21582     
21583     
21584 });
21585
21586  
21587 /*
21588  * - LGPL
21589  *
21590  * Tab pane
21591  * 
21592  */
21593 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21594 /**
21595  * @class Roo.bootstrap.TabPane
21596  * @extends Roo.bootstrap.Component
21597  * Bootstrap TabPane class
21598  * @cfg {Boolean} active (false | true) Default false
21599  * @cfg {String} title title of panel
21600
21601  * 
21602  * @constructor
21603  * Create a new TabPane
21604  * @param {Object} config The config object
21605  */
21606
21607 Roo.bootstrap.dash.TabPane = function(config){
21608     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21609     
21610     this.addEvents({
21611         // raw events
21612         /**
21613          * @event activate
21614          * When a pane is activated
21615          * @param {Roo.bootstrap.dash.TabPane} pane
21616          */
21617         "activate" : true
21618          
21619     });
21620 };
21621
21622 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21623     
21624     active : false,
21625     title : '',
21626     
21627     // the tabBox that this is attached to.
21628     tab : false,
21629      
21630     getAutoCreate : function() 
21631     {
21632         var cfg = {
21633             tag: 'div',
21634             cls: 'tab-pane'
21635         }
21636         
21637         if(this.active){
21638             cfg.cls += ' active';
21639         }
21640         
21641         return cfg;
21642     },
21643     initEvents  : function()
21644     {
21645         //Roo.log('trigger add pane handler');
21646         this.parent().fireEvent('addpane', this)
21647     },
21648     
21649      /**
21650      * Updates the tab title 
21651      * @param {String} html to set the title to.
21652      */
21653     setTitle: function(str)
21654     {
21655         if (!this.tab) {
21656             return;
21657         }
21658         this.title = str;
21659         this.tab.select('a', true).first().dom.innerHTML = str;
21660         
21661     }
21662     
21663     
21664     
21665 });
21666
21667  
21668
21669
21670  /*
21671  * - LGPL
21672  *
21673  * menu
21674  * 
21675  */
21676 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21677
21678 /**
21679  * @class Roo.bootstrap.menu.Menu
21680  * @extends Roo.bootstrap.Component
21681  * Bootstrap Menu class - container for Menu
21682  * @cfg {String} html Text of the menu
21683  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21684  * @cfg {String} icon Font awesome icon
21685  * @cfg {String} pos Menu align to (top | bottom) default bottom
21686  * 
21687  * 
21688  * @constructor
21689  * Create a new Menu
21690  * @param {Object} config The config object
21691  */
21692
21693
21694 Roo.bootstrap.menu.Menu = function(config){
21695     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21696     
21697     this.addEvents({
21698         /**
21699          * @event beforeshow
21700          * Fires before this menu is displayed
21701          * @param {Roo.bootstrap.menu.Menu} this
21702          */
21703         beforeshow : true,
21704         /**
21705          * @event beforehide
21706          * Fires before this menu is hidden
21707          * @param {Roo.bootstrap.menu.Menu} this
21708          */
21709         beforehide : true,
21710         /**
21711          * @event show
21712          * Fires after this menu is displayed
21713          * @param {Roo.bootstrap.menu.Menu} this
21714          */
21715         show : true,
21716         /**
21717          * @event hide
21718          * Fires after this menu is hidden
21719          * @param {Roo.bootstrap.menu.Menu} this
21720          */
21721         hide : true,
21722         /**
21723          * @event click
21724          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21725          * @param {Roo.bootstrap.menu.Menu} this
21726          * @param {Roo.EventObject} e
21727          */
21728         click : true
21729     });
21730     
21731 };
21732
21733 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21734     
21735     submenu : false,
21736     html : '',
21737     weight : 'default',
21738     icon : false,
21739     pos : 'bottom',
21740     
21741     
21742     getChildContainer : function() {
21743         if(this.isSubMenu){
21744             return this.el;
21745         }
21746         
21747         return this.el.select('ul.dropdown-menu', true).first();  
21748     },
21749     
21750     getAutoCreate : function()
21751     {
21752         var text = [
21753             {
21754                 tag : 'span',
21755                 cls : 'roo-menu-text',
21756                 html : this.html
21757             }
21758         ];
21759         
21760         if(this.icon){
21761             text.unshift({
21762                 tag : 'i',
21763                 cls : 'fa ' + this.icon
21764             })
21765         }
21766         
21767         
21768         var cfg = {
21769             tag : 'div',
21770             cls : 'btn-group',
21771             cn : [
21772                 {
21773                     tag : 'button',
21774                     cls : 'dropdown-button btn btn-' + this.weight,
21775                     cn : text
21776                 },
21777                 {
21778                     tag : 'button',
21779                     cls : 'dropdown-toggle btn btn-' + this.weight,
21780                     cn : [
21781                         {
21782                             tag : 'span',
21783                             cls : 'caret'
21784                         }
21785                     ]
21786                 },
21787                 {
21788                     tag : 'ul',
21789                     cls : 'dropdown-menu'
21790                 }
21791             ]
21792             
21793         };
21794         
21795         if(this.pos == 'top'){
21796             cfg.cls += ' dropup';
21797         }
21798         
21799         if(this.isSubMenu){
21800             cfg = {
21801                 tag : 'ul',
21802                 cls : 'dropdown-menu'
21803             }
21804         }
21805         
21806         return cfg;
21807     },
21808     
21809     onRender : function(ct, position)
21810     {
21811         this.isSubMenu = ct.hasClass('dropdown-submenu');
21812         
21813         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21814     },
21815     
21816     initEvents : function() 
21817     {
21818         if(this.isSubMenu){
21819             return;
21820         }
21821         
21822         this.hidden = true;
21823         
21824         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21825         this.triggerEl.on('click', this.onTriggerPress, this);
21826         
21827         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21828         this.buttonEl.on('click', this.onClick, this);
21829         
21830     },
21831     
21832     list : function()
21833     {
21834         if(this.isSubMenu){
21835             return this.el;
21836         }
21837         
21838         return this.el.select('ul.dropdown-menu', true).first();
21839     },
21840     
21841     onClick : function(e)
21842     {
21843         this.fireEvent("click", this, e);
21844     },
21845     
21846     onTriggerPress  : function(e)
21847     {   
21848         if (this.isVisible()) {
21849             this.hide();
21850         } else {
21851             this.show();
21852         }
21853     },
21854     
21855     isVisible : function(){
21856         return !this.hidden;
21857     },
21858     
21859     show : function()
21860     {
21861         this.fireEvent("beforeshow", this);
21862         
21863         this.hidden = false;
21864         this.el.addClass('open');
21865         
21866         Roo.get(document).on("mouseup", this.onMouseUp, this);
21867         
21868         this.fireEvent("show", this);
21869         
21870         
21871     },
21872     
21873     hide : function()
21874     {
21875         this.fireEvent("beforehide", this);
21876         
21877         this.hidden = true;
21878         this.el.removeClass('open');
21879         
21880         Roo.get(document).un("mouseup", this.onMouseUp);
21881         
21882         this.fireEvent("hide", this);
21883     },
21884     
21885     onMouseUp : function()
21886     {
21887         this.hide();
21888     }
21889     
21890 });
21891
21892  
21893  /*
21894  * - LGPL
21895  *
21896  * menu item
21897  * 
21898  */
21899 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21900
21901 /**
21902  * @class Roo.bootstrap.menu.Item
21903  * @extends Roo.bootstrap.Component
21904  * Bootstrap MenuItem class
21905  * @cfg {Boolean} submenu (true | false) default false
21906  * @cfg {String} html text of the item
21907  * @cfg {String} href the link
21908  * @cfg {Boolean} disable (true | false) default false
21909  * @cfg {Boolean} preventDefault (true | false) default true
21910  * @cfg {String} icon Font awesome icon
21911  * @cfg {String} pos Submenu align to (left | right) default right 
21912  * 
21913  * 
21914  * @constructor
21915  * Create a new Item
21916  * @param {Object} config The config object
21917  */
21918
21919
21920 Roo.bootstrap.menu.Item = function(config){
21921     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21922     this.addEvents({
21923         /**
21924          * @event mouseover
21925          * Fires when the mouse is hovering over this menu
21926          * @param {Roo.bootstrap.menu.Item} this
21927          * @param {Roo.EventObject} e
21928          */
21929         mouseover : true,
21930         /**
21931          * @event mouseout
21932          * Fires when the mouse exits this menu
21933          * @param {Roo.bootstrap.menu.Item} this
21934          * @param {Roo.EventObject} e
21935          */
21936         mouseout : true,
21937         // raw events
21938         /**
21939          * @event click
21940          * The raw click event for the entire grid.
21941          * @param {Roo.EventObject} e
21942          */
21943         click : true
21944     });
21945 };
21946
21947 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21948     
21949     submenu : false,
21950     href : '',
21951     html : '',
21952     preventDefault: true,
21953     disable : false,
21954     icon : false,
21955     pos : 'right',
21956     
21957     getAutoCreate : function()
21958     {
21959         var text = [
21960             {
21961                 tag : 'span',
21962                 cls : 'roo-menu-item-text',
21963                 html : this.html
21964             }
21965         ];
21966         
21967         if(this.icon){
21968             text.unshift({
21969                 tag : 'i',
21970                 cls : 'fa ' + this.icon
21971             })
21972         }
21973         
21974         var cfg = {
21975             tag : 'li',
21976             cn : [
21977                 {
21978                     tag : 'a',
21979                     href : this.href || '#',
21980                     cn : text
21981                 }
21982             ]
21983         };
21984         
21985         if(this.disable){
21986             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21987         }
21988         
21989         if(this.submenu){
21990             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21991             
21992             if(this.pos == 'left'){
21993                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21994             }
21995         }
21996         
21997         return cfg;
21998     },
21999     
22000     initEvents : function() 
22001     {
22002         this.el.on('mouseover', this.onMouseOver, this);
22003         this.el.on('mouseout', this.onMouseOut, this);
22004         
22005         this.el.select('a', true).first().on('click', this.onClick, this);
22006         
22007     },
22008     
22009     onClick : function(e)
22010     {
22011         if(this.preventDefault){
22012             e.preventDefault();
22013         }
22014         
22015         this.fireEvent("click", this, e);
22016     },
22017     
22018     onMouseOver : function(e)
22019     {
22020         if(this.submenu && this.pos == 'left'){
22021             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22022         }
22023         
22024         this.fireEvent("mouseover", this, e);
22025     },
22026     
22027     onMouseOut : function(e)
22028     {
22029         this.fireEvent("mouseout", this, e);
22030     }
22031 });
22032
22033  
22034
22035  /*
22036  * - LGPL
22037  *
22038  * menu separator
22039  * 
22040  */
22041 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22042
22043 /**
22044  * @class Roo.bootstrap.menu.Separator
22045  * @extends Roo.bootstrap.Component
22046  * Bootstrap Separator class
22047  * 
22048  * @constructor
22049  * Create a new Separator
22050  * @param {Object} config The config object
22051  */
22052
22053
22054 Roo.bootstrap.menu.Separator = function(config){
22055     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22059     
22060     getAutoCreate : function(){
22061         var cfg = {
22062             tag : 'li',
22063             cls: 'divider'
22064         };
22065         
22066         return cfg;
22067     }
22068    
22069 });
22070
22071  
22072
22073  /*
22074  * - LGPL
22075  *
22076  * Tooltip
22077  * 
22078  */
22079
22080 /**
22081  * @class Roo.bootstrap.Tooltip
22082  * Bootstrap Tooltip class
22083  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22084  * to determine which dom element triggers the tooltip.
22085  * 
22086  * It needs to add support for additional attributes like tooltip-position
22087  * 
22088  * @constructor
22089  * Create a new Toolti
22090  * @param {Object} config The config object
22091  */
22092
22093 Roo.bootstrap.Tooltip = function(config){
22094     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22095 };
22096
22097 Roo.apply(Roo.bootstrap.Tooltip, {
22098     /**
22099      * @function init initialize tooltip monitoring.
22100      * @static
22101      */
22102     currentEl : false,
22103     currentTip : false,
22104     currentRegion : false,
22105     
22106     //  init : delay?
22107     
22108     init : function()
22109     {
22110         Roo.get(document).on('mouseover', this.enter ,this);
22111         Roo.get(document).on('mouseout', this.leave, this);
22112          
22113         
22114         this.currentTip = new Roo.bootstrap.Tooltip();
22115     },
22116     
22117     enter : function(ev)
22118     {
22119         var dom = ev.getTarget();
22120         
22121         //Roo.log(['enter',dom]);
22122         var el = Roo.fly(dom);
22123         if (this.currentEl) {
22124             //Roo.log(dom);
22125             //Roo.log(this.currentEl);
22126             //Roo.log(this.currentEl.contains(dom));
22127             if (this.currentEl == el) {
22128                 return;
22129             }
22130             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22131                 return;
22132             }
22133
22134         }
22135         
22136         
22137         
22138         if (this.currentTip.el) {
22139             this.currentTip.el.hide(); // force hiding...
22140         }    
22141         //Roo.log(ev);
22142         var bindEl = el;
22143         
22144         // you can not look for children, as if el is the body.. then everythign is the child..
22145         if (!el.attr('tooltip')) { //
22146             if (!el.select("[tooltip]").elements.length) {
22147                 return;
22148             }
22149             // is the mouse over this child...?
22150             bindEl = el.select("[tooltip]").first();
22151             var xy = ev.getXY();
22152             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22153                 //Roo.log("not in region.");
22154                 return;
22155             }
22156             //Roo.log("child element over..");
22157             
22158         }
22159         this.currentEl = bindEl;
22160         this.currentTip.bind(bindEl);
22161         this.currentRegion = Roo.lib.Region.getRegion(dom);
22162         this.currentTip.enter();
22163         
22164     },
22165     leave : function(ev)
22166     {
22167         var dom = ev.getTarget();
22168         //Roo.log(['leave',dom]);
22169         if (!this.currentEl) {
22170             return;
22171         }
22172         
22173         
22174         if (dom != this.currentEl.dom) {
22175             return;
22176         }
22177         var xy = ev.getXY();
22178         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22179             return;
22180         }
22181         // only activate leave if mouse cursor is outside... bounding box..
22182         
22183         
22184         
22185         
22186         if (this.currentTip) {
22187             this.currentTip.leave();
22188         }
22189         //Roo.log('clear currentEl');
22190         this.currentEl = false;
22191         
22192         
22193     },
22194     alignment : {
22195         'left' : ['r-l', [-2,0], 'right'],
22196         'right' : ['l-r', [2,0], 'left'],
22197         'bottom' : ['t-b', [0,2], 'top'],
22198         'top' : [ 'b-t', [0,-2], 'bottom']
22199     }
22200     
22201 });
22202
22203
22204 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22205     
22206     
22207     bindEl : false,
22208     
22209     delay : null, // can be { show : 300 , hide: 500}
22210     
22211     timeout : null,
22212     
22213     hoverState : null, //???
22214     
22215     placement : 'bottom', 
22216     
22217     getAutoCreate : function(){
22218     
22219         var cfg = {
22220            cls : 'tooltip',
22221            role : 'tooltip',
22222            cn : [
22223                 {
22224                     cls : 'tooltip-arrow'
22225                 },
22226                 {
22227                     cls : 'tooltip-inner'
22228                 }
22229            ]
22230         };
22231         
22232         return cfg;
22233     },
22234     bind : function(el)
22235     {
22236         this.bindEl = el;
22237     },
22238       
22239     
22240     enter : function () {
22241        
22242         if (this.timeout != null) {
22243             clearTimeout(this.timeout);
22244         }
22245         
22246         this.hoverState = 'in';
22247          //Roo.log("enter - show");
22248         if (!this.delay || !this.delay.show) {
22249             this.show();
22250             return;
22251         }
22252         var _t = this;
22253         this.timeout = setTimeout(function () {
22254             if (_t.hoverState == 'in') {
22255                 _t.show();
22256             }
22257         }, this.delay.show);
22258     },
22259     leave : function()
22260     {
22261         clearTimeout(this.timeout);
22262     
22263         this.hoverState = 'out';
22264          if (!this.delay || !this.delay.hide) {
22265             this.hide();
22266             return;
22267         }
22268        
22269         var _t = this;
22270         this.timeout = setTimeout(function () {
22271             //Roo.log("leave - timeout");
22272             
22273             if (_t.hoverState == 'out') {
22274                 _t.hide();
22275                 Roo.bootstrap.Tooltip.currentEl = false;
22276             }
22277         }, delay);
22278     },
22279     
22280     show : function ()
22281     {
22282         if (!this.el) {
22283             this.render(document.body);
22284         }
22285         // set content.
22286         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22287         
22288         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22289         
22290         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22291         
22292         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22293         
22294         var placement = typeof this.placement == 'function' ?
22295             this.placement.call(this, this.el, on_el) :
22296             this.placement;
22297             
22298         var autoToken = /\s?auto?\s?/i;
22299         var autoPlace = autoToken.test(placement);
22300         if (autoPlace) {
22301             placement = placement.replace(autoToken, '') || 'top';
22302         }
22303         
22304         //this.el.detach()
22305         //this.el.setXY([0,0]);
22306         this.el.show();
22307         //this.el.dom.style.display='block';
22308         this.el.addClass(placement);
22309         
22310         //this.el.appendTo(on_el);
22311         
22312         var p = this.getPosition();
22313         var box = this.el.getBox();
22314         
22315         if (autoPlace) {
22316             // fixme..
22317         }
22318         var align = Roo.bootstrap.Tooltip.alignment[placement];
22319         this.el.alignTo(this.bindEl, align[0],align[1]);
22320         //var arrow = this.el.select('.arrow',true).first();
22321         //arrow.set(align[2], 
22322         
22323         this.el.addClass('in fade');
22324         this.hoverState = null;
22325         
22326         if (this.el.hasClass('fade')) {
22327             // fade it?
22328         }
22329         
22330     },
22331     hide : function()
22332     {
22333          
22334         if (!this.el) {
22335             return;
22336         }
22337         //this.el.setXY([0,0]);
22338         this.el.removeClass('in');
22339         //this.el.hide();
22340         
22341     }
22342     
22343 });
22344  
22345
22346  /*
22347  * - LGPL
22348  *
22349  * Location Picker
22350  * 
22351  */
22352
22353 /**
22354  * @class Roo.bootstrap.LocationPicker
22355  * @extends Roo.bootstrap.Component
22356  * Bootstrap LocationPicker class
22357  * @cfg {Number} latitude Position when init default 0
22358  * @cfg {Number} longitude Position when init default 0
22359  * @cfg {Number} zoom default 15
22360  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22361  * @cfg {Boolean} mapTypeControl default false
22362  * @cfg {Boolean} disableDoubleClickZoom default false
22363  * @cfg {Boolean} scrollwheel default true
22364  * @cfg {Boolean} streetViewControl default false
22365  * @cfg {Number} radius default 0
22366  * @cfg {String} locationName
22367  * @cfg {Boolean} draggable default true
22368  * @cfg {Boolean} enableAutocomplete default false
22369  * @cfg {Boolean} enableReverseGeocode default true
22370  * @cfg {String} markerTitle
22371  * 
22372  * @constructor
22373  * Create a new LocationPicker
22374  * @param {Object} config The config object
22375  */
22376
22377
22378 Roo.bootstrap.LocationPicker = function(config){
22379     
22380     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22381     
22382     this.addEvents({
22383         /**
22384          * @event initial
22385          * Fires when the picker initialized.
22386          * @param {Roo.bootstrap.LocationPicker} this
22387          * @param {Google Location} location
22388          */
22389         initial : true,
22390         /**
22391          * @event positionchanged
22392          * Fires when the picker position changed.
22393          * @param {Roo.bootstrap.LocationPicker} this
22394          * @param {Google Location} location
22395          */
22396         positionchanged : true,
22397         /**
22398          * @event resize
22399          * Fires when the map resize.
22400          * @param {Roo.bootstrap.LocationPicker} this
22401          */
22402         resize : true,
22403         /**
22404          * @event show
22405          * Fires when the map show.
22406          * @param {Roo.bootstrap.LocationPicker} this
22407          */
22408         show : true,
22409         /**
22410          * @event hide
22411          * Fires when the map hide.
22412          * @param {Roo.bootstrap.LocationPicker} this
22413          */
22414         hide : true,
22415         /**
22416          * @event mapClick
22417          * Fires when click the map.
22418          * @param {Roo.bootstrap.LocationPicker} this
22419          * @param {Map event} e
22420          */
22421         mapClick : true,
22422         /**
22423          * @event mapRightClick
22424          * Fires when right click the map.
22425          * @param {Roo.bootstrap.LocationPicker} this
22426          * @param {Map event} e
22427          */
22428         mapRightClick : true,
22429         /**
22430          * @event markerClick
22431          * Fires when click the marker.
22432          * @param {Roo.bootstrap.LocationPicker} this
22433          * @param {Map event} e
22434          */
22435         markerClick : true,
22436         /**
22437          * @event markerRightClick
22438          * Fires when right click the marker.
22439          * @param {Roo.bootstrap.LocationPicker} this
22440          * @param {Map event} e
22441          */
22442         markerRightClick : true,
22443         /**
22444          * @event OverlayViewDraw
22445          * Fires when OverlayView Draw
22446          * @param {Roo.bootstrap.LocationPicker} this
22447          */
22448         OverlayViewDraw : true,
22449         /**
22450          * @event OverlayViewOnAdd
22451          * Fires when OverlayView Draw
22452          * @param {Roo.bootstrap.LocationPicker} this
22453          */
22454         OverlayViewOnAdd : true,
22455         /**
22456          * @event OverlayViewOnRemove
22457          * Fires when OverlayView Draw
22458          * @param {Roo.bootstrap.LocationPicker} this
22459          */
22460         OverlayViewOnRemove : true,
22461         /**
22462          * @event OverlayViewShow
22463          * Fires when OverlayView Draw
22464          * @param {Roo.bootstrap.LocationPicker} this
22465          * @param {Pixel} cpx
22466          */
22467         OverlayViewShow : true,
22468         /**
22469          * @event OverlayViewHide
22470          * Fires when OverlayView Draw
22471          * @param {Roo.bootstrap.LocationPicker} this
22472          */
22473         OverlayViewHide : true
22474     });
22475         
22476 };
22477
22478 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22479     
22480     gMapContext: false,
22481     
22482     latitude: 0,
22483     longitude: 0,
22484     zoom: 15,
22485     mapTypeId: false,
22486     mapTypeControl: false,
22487     disableDoubleClickZoom: false,
22488     scrollwheel: true,
22489     streetViewControl: false,
22490     radius: 0,
22491     locationName: '',
22492     draggable: true,
22493     enableAutocomplete: false,
22494     enableReverseGeocode: true,
22495     markerTitle: '',
22496     
22497     getAutoCreate: function()
22498     {
22499
22500         var cfg = {
22501             tag: 'div',
22502             cls: 'roo-location-picker'
22503         };
22504         
22505         return cfg
22506     },
22507     
22508     initEvents: function(ct, position)
22509     {       
22510         if(!this.el.getWidth() || this.isApplied()){
22511             return;
22512         }
22513         
22514         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22515         
22516         this.initial();
22517     },
22518     
22519     initial: function()
22520     {
22521         if(!this.mapTypeId){
22522             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22523         }
22524         
22525         this.gMapContext = this.GMapContext();
22526         
22527         this.initOverlayView();
22528         
22529         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22530         
22531         var _this = this;
22532                 
22533         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22534             _this.setPosition(_this.gMapContext.marker.position);
22535         });
22536         
22537         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22538             _this.fireEvent('mapClick', this, event);
22539             
22540         });
22541
22542         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22543             _this.fireEvent('mapRightClick', this, event);
22544             
22545         });
22546         
22547         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22548             _this.fireEvent('markerClick', this, event);
22549             
22550         });
22551
22552         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22553             _this.fireEvent('markerRightClick', this, event);
22554             
22555         });
22556         
22557         this.setPosition(this.gMapContext.location);
22558         
22559         this.fireEvent('initial', this, this.gMapContext.location);
22560     },
22561     
22562     initOverlayView: function()
22563     {
22564         var _this = this;
22565         
22566         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22567             
22568             draw: function()
22569             {
22570                 _this.fireEvent('OverlayViewDraw', _this);
22571             },
22572             
22573             onAdd: function()
22574             {
22575                 _this.fireEvent('OverlayViewOnAdd', _this);
22576             },
22577             
22578             onRemove: function()
22579             {
22580                 _this.fireEvent('OverlayViewOnRemove', _this);
22581             },
22582             
22583             show: function(cpx)
22584             {
22585                 _this.fireEvent('OverlayViewShow', _this, cpx);
22586             },
22587             
22588             hide: function()
22589             {
22590                 _this.fireEvent('OverlayViewHide', _this);
22591             }
22592             
22593         });
22594     },
22595     
22596     fromLatLngToContainerPixel: function(event)
22597     {
22598         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22599     },
22600     
22601     isApplied: function() 
22602     {
22603         return this.getGmapContext() == false ? false : true;
22604     },
22605     
22606     getGmapContext: function() 
22607     {
22608         return this.gMapContext
22609     },
22610     
22611     GMapContext: function() 
22612     {
22613         var position = new google.maps.LatLng(this.latitude, this.longitude);
22614         
22615         var _map = new google.maps.Map(this.el.dom, {
22616             center: position,
22617             zoom: this.zoom,
22618             mapTypeId: this.mapTypeId,
22619             mapTypeControl: this.mapTypeControl,
22620             disableDoubleClickZoom: this.disableDoubleClickZoom,
22621             scrollwheel: this.scrollwheel,
22622             streetViewControl: this.streetViewControl,
22623             locationName: this.locationName,
22624             draggable: this.draggable,
22625             enableAutocomplete: this.enableAutocomplete,
22626             enableReverseGeocode: this.enableReverseGeocode
22627         });
22628         
22629         var _marker = new google.maps.Marker({
22630             position: position,
22631             map: _map,
22632             title: this.markerTitle,
22633             draggable: this.draggable
22634         });
22635         
22636         return {
22637             map: _map,
22638             marker: _marker,
22639             circle: null,
22640             location: position,
22641             radius: this.radius,
22642             locationName: this.locationName,
22643             addressComponents: {
22644                 formatted_address: null,
22645                 addressLine1: null,
22646                 addressLine2: null,
22647                 streetName: null,
22648                 streetNumber: null,
22649                 city: null,
22650                 district: null,
22651                 state: null,
22652                 stateOrProvince: null
22653             },
22654             settings: this,
22655             domContainer: this.el.dom,
22656             geodecoder: new google.maps.Geocoder()
22657         };
22658     },
22659     
22660     drawCircle: function(center, radius, options) 
22661     {
22662         if (this.gMapContext.circle != null) {
22663             this.gMapContext.circle.setMap(null);
22664         }
22665         if (radius > 0) {
22666             radius *= 1;
22667             options = Roo.apply({}, options, {
22668                 strokeColor: "#0000FF",
22669                 strokeOpacity: .35,
22670                 strokeWeight: 2,
22671                 fillColor: "#0000FF",
22672                 fillOpacity: .2
22673             });
22674             
22675             options.map = this.gMapContext.map;
22676             options.radius = radius;
22677             options.center = center;
22678             this.gMapContext.circle = new google.maps.Circle(options);
22679             return this.gMapContext.circle;
22680         }
22681         
22682         return null;
22683     },
22684     
22685     setPosition: function(location) 
22686     {
22687         this.gMapContext.location = location;
22688         this.gMapContext.marker.setPosition(location);
22689         this.gMapContext.map.panTo(location);
22690         this.drawCircle(location, this.gMapContext.radius, {});
22691         
22692         var _this = this;
22693         
22694         if (this.gMapContext.settings.enableReverseGeocode) {
22695             this.gMapContext.geodecoder.geocode({
22696                 latLng: this.gMapContext.location
22697             }, function(results, status) {
22698                 
22699                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22700                     _this.gMapContext.locationName = results[0].formatted_address;
22701                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22702                     
22703                     _this.fireEvent('positionchanged', this, location);
22704                 }
22705             });
22706             
22707             return;
22708         }
22709         
22710         this.fireEvent('positionchanged', this, location);
22711     },
22712     
22713     resize: function()
22714     {
22715         google.maps.event.trigger(this.gMapContext.map, "resize");
22716         
22717         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22718         
22719         this.fireEvent('resize', this);
22720     },
22721     
22722     setPositionByLatLng: function(latitude, longitude)
22723     {
22724         this.setPosition(new google.maps.LatLng(latitude, longitude));
22725     },
22726     
22727     getCurrentPosition: function() 
22728     {
22729         return {
22730             latitude: this.gMapContext.location.lat(),
22731             longitude: this.gMapContext.location.lng()
22732         };
22733     },
22734     
22735     getAddressName: function() 
22736     {
22737         return this.gMapContext.locationName;
22738     },
22739     
22740     getAddressComponents: function() 
22741     {
22742         return this.gMapContext.addressComponents;
22743     },
22744     
22745     address_component_from_google_geocode: function(address_components) 
22746     {
22747         var result = {};
22748         
22749         for (var i = 0; i < address_components.length; i++) {
22750             var component = address_components[i];
22751             if (component.types.indexOf("postal_code") >= 0) {
22752                 result.postalCode = component.short_name;
22753             } else if (component.types.indexOf("street_number") >= 0) {
22754                 result.streetNumber = component.short_name;
22755             } else if (component.types.indexOf("route") >= 0) {
22756                 result.streetName = component.short_name;
22757             } else if (component.types.indexOf("neighborhood") >= 0) {
22758                 result.city = component.short_name;
22759             } else if (component.types.indexOf("locality") >= 0) {
22760                 result.city = component.short_name;
22761             } else if (component.types.indexOf("sublocality") >= 0) {
22762                 result.district = component.short_name;
22763             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22764                 result.stateOrProvince = component.short_name;
22765             } else if (component.types.indexOf("country") >= 0) {
22766                 result.country = component.short_name;
22767             }
22768         }
22769         
22770         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22771         result.addressLine2 = "";
22772         return result;
22773     },
22774     
22775     setZoomLevel: function(zoom)
22776     {
22777         this.gMapContext.map.setZoom(zoom);
22778     },
22779     
22780     show: function()
22781     {
22782         if(!this.el){
22783             return;
22784         }
22785         
22786         this.el.show();
22787         
22788         this.resize();
22789         
22790         this.fireEvent('show', this);
22791     },
22792     
22793     hide: function()
22794     {
22795         if(!this.el){
22796             return;
22797         }
22798         
22799         this.el.hide();
22800         
22801         this.fireEvent('hide', this);
22802     }
22803     
22804 });
22805
22806 Roo.apply(Roo.bootstrap.LocationPicker, {
22807     
22808     OverlayView : function(map, options)
22809     {
22810         options = options || {};
22811         
22812         this.setMap(map);
22813     }
22814     
22815     
22816 });/*
22817  * - LGPL
22818  *
22819  * Alert
22820  * 
22821  */
22822
22823 /**
22824  * @class Roo.bootstrap.Alert
22825  * @extends Roo.bootstrap.Component
22826  * Bootstrap Alert class
22827  * @cfg {String} title The title of alert
22828  * @cfg {String} html The content of alert
22829  * @cfg {String} weight (  success | info | warning | danger )
22830  * @cfg {String} faicon font-awesomeicon
22831  * 
22832  * @constructor
22833  * Create a new alert
22834  * @param {Object} config The config object
22835  */
22836
22837
22838 Roo.bootstrap.Alert = function(config){
22839     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22840     
22841 };
22842
22843 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22844     
22845     title: '',
22846     html: '',
22847     weight: false,
22848     faicon: false,
22849     
22850     getAutoCreate : function()
22851     {
22852         
22853         var cfg = {
22854             tag : 'div',
22855             cls : 'alert',
22856             cn : [
22857                 {
22858                     tag : 'i',
22859                     cls : 'roo-alert-icon'
22860                     
22861                 },
22862                 {
22863                     tag : 'b',
22864                     cls : 'roo-alert-title',
22865                     html : this.title
22866                 },
22867                 {
22868                     tag : 'span',
22869                     cls : 'roo-alert-text',
22870                     html : this.html
22871                 }
22872             ]
22873         };
22874         
22875         if(this.faicon){
22876             cfg.cn[0].cls += ' fa ' + this.faicon;
22877         }
22878         
22879         if(this.weight){
22880             cfg.cls += ' alert-' + this.weight;
22881         }
22882         
22883         return cfg;
22884     },
22885     
22886     initEvents: function() 
22887     {
22888         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22889     },
22890     
22891     setTitle : function(str)
22892     {
22893         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22894     },
22895     
22896     setText : function(str)
22897     {
22898         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22899     },
22900     
22901     setWeight : function(weight)
22902     {
22903         if(this.weight){
22904             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22905         }
22906         
22907         this.weight = weight;
22908         
22909         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22910     },
22911     
22912     setIcon : function(icon)
22913     {
22914         if(this.faicon){
22915             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22916         }
22917         
22918         this.faicon = icon
22919         
22920         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22921     },
22922     
22923     hide: function() 
22924     {
22925         this.el.hide();   
22926     },
22927     
22928     show: function() 
22929     {  
22930         this.el.show();   
22931     }
22932     
22933 });
22934
22935